14:26:48 不想让 你为难,你不再需要给我个答案。——杨宗纬《空白格》
第一题 【NOIP2018模拟赛】刺客信条(AC)
分四个情况,用一个并查集维护联通性
sprt()用于算平方根
#include<bits/stdc++.h> const int N=2002; using namespace std; char ch; struct sby{ int x,y; double v; }t[N*N]; int n,x[N],y[N],f[N],xx,yy,tot,ans; void add(int x,int y,double v) { t[++tot].x=x; t[tot].y=y; t[tot].v=v; } bool cmp(sby a,sby b){return a.v<b.v;} int ss(int x){return f[x]=(f[x]^x)?ss(f[x]):x;} int main() { cin>>xx>>yy>>n; for(int i=1;i<=n;i++) { cin>>x[i]>>y[i]; f[i]=i; add(n+1,i,y[i]);add(n+1,i,xx-x[i]); add(n+2,i,x[i]);add(n+2,i,yy-y[i]); for(int j=1;j<i;j++) add(i,j,sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(double)(y[i]-y[j])*(y[i]-y[j]))/2); } f[n+1]=n+1;f[n+2]=n+2; sort(t+1,t+1+tot,cmp); for(ans=1;ss(n+1)^ss(n+2);ans++)f[ss(t[ans].x)]=ss(t[ans].y); printf("%.2lf",t[ans-1].v); return 0; }
第二题 【NOIP2018模拟赛】传送门 (portal)
假设我们当前节点为i
要么在当前节点设传送门,暴力走p,再传回来,很明显我们会从最深的那个叶子传回来。
要么在p的子树中设传送门,暴力走这条边2次(下去1次,回来1次)
a[i]表示i子树中到i最远的叶子。
最后答案就是f[1]
#include<bits/stdc++.h> #define N 1000005 #define ll long long using namespace std; ll f[N],g[N],a[N],c[2*N],b[2*N],head[N],n,m,next[2*N]; void add(int x,int y,int z){next[++m]=head[x];b[head[x]=m]=y;c[m]=z;} void dfs(int k,int o) { f[k]=0,g[k]=0; ll v=0; for(int i=head[k];i;i=next[i]) { int p=b[i]; if(p!=o) { dfs(p,k); g[k]=g[k]+2*c[i]+g[p]; a[k]=max(a[k],a[p]+c[i]); f[k]=f[k]+min(2*c[i]+f[p],g[p]+c[i]-a[p]); } } return; } int main() { cin>>n; for(int i=1;i<n;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); } dfs(1,0); printf("%lld ",f[1]); }
第三题【NOIP2018模拟赛】黑暗之魂(darksoul)
代码繁琐。思想简单。
求图的直径。
环套树,记dis(x,y)为环上的点x到点y的最短路径。
显然,给环定一个方向,求个前缀和。对于每个环上的点x,必有一个a[x],表示按照给定的方向走,第一个满足环长显然不递减。
所以拿个线段树维护一下。一个区间加,一个区间减。
#include<bits/stdc++.h> #define ll long long const int N=1e6+7; using namespace std; int n,cnt,cir,cc,tot,mid,head,tail,flag; int ls[N],ss[N],low[N],bb[N]; bool vis[N]; int id[N],q[N*2]; ll ans,f[N*2],size[N*2],s[N*2],dis[N*2],sumdis; struct xz{ int y,w,next; }g[N*2]; struct sby{ int x,y,w; }a[N]; vector <int> wyb; void add(int x,int y,int w) { g[++cnt]=(xz){y,w,ls[x]}; ls[x]=cnt; } void tarjan(int x,int fa) { ss[x]=low[x]=++cnt; vis[x]=1; wyb.push_back(x); for (int i=ls[x];i>0;i=g[i].next) { int y=g[i].y; if(y==fa) continue; if(!ss[y]) { tarjan(y,x); low[x]=min(low[x],low[y]); } else { if(vis[y]) low[x]=min(low[x],ss[y]); } } if(low[x]==ss[x]) { cc++; int y=0,flag=0; if (wyb.back()!=x) flag=1,cir=cc; while (y!=x) { y=wyb.back(); wyb.pop_back(); bb[y]=cc; vis[y]=0; if (flag) id[++tot]=y; } } } void dfs(int x,int fa) { ll max1=0,max2=0; for (int i=ls[x];i>0;i=g[i].next) { int y=g[i].y; if ((bb[y]==cir) || (y==fa)) continue; dfs(y,x); ll d=f[y]+g[i].w; if (d>max1) { max2=max1; max1=d; } else if (d>max2) max2=d; } ans=max(ans,max1+max2); f[x]=max1; } bool cmp(sby a,sby b) { if (a.x==b.x) { if (a.y==b.y) return a.w<b.w; return a.y<b.y; } return a.x<b.x; } int main() { cin>>n; for (int i=1;i<=n;i++) { cin>>a[i].x>>a[i].y>>a[i].w; if (a[i].x>a[i].y) swap(a[i].x,a[i].y); } sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) { if(a[i].x==a[i].y) flag=1; else { if((a[i].x==a[i-1].x)&&(a[i].y==a[i-1].y)) flag=1; else { add(a[i].x,a[i].y,a[i].w); add(a[i].y,a[i].x,a[i].w); } } } if (flag) { cir=1; dfs(1,0); printf("%lld",ans+1); return 0; } tarjan(1,0); for(int i=1;i<=tot;i++) dfs(id[i],0),size[i]=f[id[i]]; for(int i=1;i<=tot;i++) { int d; if(i==1) d=id[tot]; else d=id[i-1]; for(int j=ls[id[i]];j>0;j=g[j].next) { if(g[j].y==d) { dis[i]=(ll)g[j].w; break; } } } for(int i=tot+1;i<=2*tot;i++) dis[i]=dis[i-tot],size[i]=size[i-tot]; for(int i=1;i<=2*tot;i++) s[i]=s[i-1]+dis[i]; sumdis=s[tot]; head=tail=1; q[head]=1; for (int i=2;i<=2*tot;i++) { while ((head<=tail) && (s[i]-s[q[head]]>sumdis/2)) head++; if (head<=tail) ans=max(ans,size[i]+s[i]+size[q[head]]-s[q[head]]); while ((head<=tail) && (size[q[tail]]-s[q[tail]]<=size[i]-s[i])) tail--; q[++tail]=i; } printf("%lld",ans+1); return 0; }