解决本题分为两个部分:维护树的直径,合并多个树的直径
树的直径有如下性质:
1,从任一点出发,到达最远的点是直径的其中一端,从这一点出发可以到达最远的点是直径的另一端。或者说一棵树中距离某一点最远的点一定是直径的一端。
2,由1,两个树通过一条边连接形成的新的树的直径是两棵树直径4个端点的两两组合之一。
所以对于维护树的直径,可以维护每一棵树直径的两个端点,合并时用\(LCT\)的\(split\)或者离线+\(LCA\)暴力判断。
合并多棵树的直径时,合并后的直径有3种可能:
1.直径最长的树的直径
2.直径最长和次长树合并后直径
3.直径次长和第3场树合并在直径最长的树上后点的直径
用堆维护每一棵树的直径然后判断即可。
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> using namespace std; #define N 101000 priority_queue<int> q; int sum[N],fa[N],ch[N][2],val[N],rev[N],stack[N]; int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return sum*f; } void update(int now){ sum[now]=sum[ch[now][0]]+sum[ch[now][1]]+val[now]; } bool son(int now){ return ch[fa[now]][1]==now; } bool isroot(int now){ return ch[fa[now]][0]!=now&&ch[fa[now]][1]!=now; } void rotate(int x){ int y=fa[x],z=fa[y],a=son(x),b=son(y),s=ch[x][!a]; if(!isroot(y))ch[z][b]=x;fa[x]=z; if(s)fa[s]=y;ch[y][a]=s; fa[y]=x;ch[x][!a]=y; update(y),update(x); } void rever(int now){ swap(ch[now][0],ch[now][1]); rev[now]^=1; } void pushdown(int now){ if(rev[now]==0)return; if(ch[now][0])rever(ch[now][0]); if(ch[now][1])rever(ch[now][1]); rev[now]=0; } void splay(int x){ int now=x,top=0; while(!isroot(now))stack[++top]=now,now=fa[now]; stack[++top]=now; while(top)pushdown(stack[top--]); while(!isroot(x)){ int y=fa[x]; if(isroot(y))rotate(x); else{ rotate(son(x)==son(y)?y:x); rotate(x); } } } void access(int now){ for(int x=now,y=0;x;y=x,x=fa[x]) splay(x),ch[x][1]=y,update(x); } void makeroot(int now){//相当于now与root路径上的点收尾交换,对应虚儿子也跟随变动。 access(now); splay(now); rever(now); } int findroot(int now){ access(now); splay(now); while(ch[now][0])pushdown(now),now=ch[now][0]; return now; } void split(int x,int y){ makeroot(x); access(y); splay(y); } void link(int x,int y){ makeroot(x); if(findroot(y)!=x)fa[x]=y; } int f[N],mx[N][2],len[N],Map[N]; int find(int x){ if(f[x]==x)return x; else return f[x]=find(f[x]); } int main(){ int T=read(); while(T--){ int n=read(),m=read(); for(int i=1;i<=n;i++)val[i]=sum[i]=1,q.push(1); for(int i=1;i<=n;i++){ f[i]=i; mx[i][0]=mx[i][1]=i; len[i]=1; } while(m--){ int x=read(),y=read(); link(x,y); int fx=find(x),fy=find(y); Map[len[fx]]++;Map[len[fy]]++; int mx_len=0; int ans_a,ans_b; if(len[fx]>len[fy]){ ans_a=mx[fx][0],ans_b=mx[fx][1]; mx_len=len[fx]; } else{ ans_a=mx[fy][0],ans_b=mx[fy][1]; mx_len=len[fy]; } for(int i=0;i<=1;i++) for(int j=0;j<=1;j++){ int a=mx[fx][i],b=mx[fy][j]; split(a,b); if(sum[b]>mx_len){ ans_a=a,ans_b=b; mx_len=sum[b]; } } f[fy]=fx; mx[fx][0]=ans_a,mx[fx][1]=ans_b; len[fx]=mx_len; q.push(len[fx]); int len_a=-1,len_b=-1,len_c=-1; while(!q.empty()){ len_a=q.top(); q.pop(); if(Map[len_a]==0)break; else Map[len_a]--,len_a=-1; } while(!q.empty()){ len_b=q.top(); q.pop(); if(Map[len_b]==0)break; else Map[len_b]--,len_b=-1; } while(!q.empty()){ len_c=q.top(); q.pop(); if(Map[len_c]==0)break; else Map[len_c]--,len_c=-1; } if(len_b==-1&&len_c==-1)printf("%d\n",len_a-1); else if(len_c==-1)printf("%d\n",max(len_a-1,len_a/2+len_b/2+1)); else printf("%d\n",max(len_a-1,max(len_a/2+len_b/2+1,len_b/2+len_c/2+2))); q.push(len_a); if(len_b!=-1)q.push(len_b); if(len_c!=-1)q.push(len_c); } while(!q.empty()){ Map[q.top()]=0; q.pop(); } for(int i=1;i<=n;i++)fa[i]=0,sum[i]=0,val[i]=0,ch[i][0]=ch[i][1]=0,rev[i]=0; } return 0; }