解决本题分为两个部分:维护树的直径,合并多个树的直径
树的直径有如下性质:
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;
}
标签:Diameter,杭电多校,fx,len,Minimum,ans,直径,Map,mx
From: https://www.cnblogs.com/Xu-daxia/p/16610664.html