【#260 div 1 C. Civilization】
【原题】
Andrew plays a game called "Civilization". Dima helps him.
The game has n cities and m bidirectional roads. The cities are numbered from 1 to n. Between any pair of cities there either is a single (unique) path, or there is no path at all. A path is such a sequence of distinct cities v1, v2, ..., vk, that there is a road between any contiguous cities vi andvi + 1 (1 ≤ i < k). The length of the described path equals to (k - 1). We assume that two cities lie in the same region if and only if, there is a path connecting these two cities.
During the game events of two types take place:
Dima finds it hard to execute Andrew's queries, so he asks you to help him. Help Dima.
The first line contains three integers n, m, q (1 ≤ n ≤ 3·105; 0 ≤ m < n; 1 ≤ q ≤ 3·105) — the number of cities, the number of the roads we already have and the number of queries, correspondingly.
Each of the following m lines contains two integers, ai and bi (ai ≠ bi; 1 ≤ ai, bi ≤ n). These numbers represent the road between cities ai andbi. There can be at most one road between two cities.
Each of the following q lines contains one of the two events in the following format:
For each event of the first type print the answer on a separate line.
6 0 6 2 1 2 2 3 4 2 5 6 2 3 2 2 5 3 1 1
4
【题意】开始有N个彼此独立的点。每次有两个操作:查询x所在的树的直径;合并x和y所在的树使新的树直径最小。
【分析】拿到手以为是神题,然后愉快地被SKYDEC大爷喷了。
如图,假设红线是两棵树的直径(用一个数组维护)。现在我要以最佳的方式来连接使直径最小。
假设我们把两个连接点都设在原树的直径上。
显然我们要把直径的中点都连起来,这样子两条路线的构成的4种直径(红/蓝->绿->红/蓝)的max会最小。
可以证明,如果所取的连接点不在原树的直径上,结果不会更优。设某个原树的连接点是P,原直径是A和B,且P不在A和B上,那么max(dis(P,A),dis(P,B))一定大于等于直径的一半。
当然还要注意一些细节。
①如果直径是奇数,一半的话是除以2加1。
②连接两树还需要额外的1的边。
③取新树直径的时候,别忘了和原树的两个直径取max。
【代码】
#include<cstdio> #include<algorithm> #define N 300005 using namespace std; struct arr{int go,next;}a[N*2]; int end[N],visit[N],d[N],ans[N],f[N]; int n,m,Q,i,x,y,z,father,now,num,cnt,opt; inline void add(int u,int v){a[++cnt].go=v;a[cnt].next=end[u];end[u]=cnt;} void dfs(int k) { visit[k]=num;f[k]=father; if (d[k]>d[now]) now=k; for (int i=end[k];i;i=a[i].next) { int go=a[i].go;if (visit[go]==num) continue; d[go]=d[k]+1;dfs(go); } } int get(int u){return f[u]==u?u:f[u]=get(f[u]);} int main() { scanf("%d%d%d",&n,&m,&Q); for (i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); for (i=1;i<=n;i++) if (!visit[i]) { father=i; now=i;num=1;d[i]=0;dfs(i); num=2;d[now]=0;dfs(now); ans[father]=d[now]; } while (Q--) { scanf("%d%d",&opt,&x); if (opt==1) {printf("%d\n",ans[get(x)]);continue;} scanf("%d",&y);x=get(x);y=get(y);if (x==y) continue; f[y]=x;ans[x]=max(max(ans[y],ans[x]),(ans[x]+1)/2+(ans[y]+1)/2+1); } return 0; }