Java教程

2022杭电多校第十场1008 Minimum Diameter(树的直径的一些性质)

本文主要是介绍2022杭电多校第十场1008 Minimum Diameter(树的直径的一些性质),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

解决本题分为两个部分:维护树的直径,合并多个树的直径

树的直径有如下性质:
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;
} 
这篇关于2022杭电多校第十场1008 Minimum Diameter(树的直径的一些性质)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!