2022“杭电杯”中国大学生算法设计超级联赛(10)部分题题解

Minimum Diameter

趁着这个题,得赶紧把关于直径的知识点整理一下。
众所周知,直径有两种求法,一种是DP的方法,一种是两遍bfs/dfs。
对于直径以下知识点需要知道:
若干条直径一定经过中点。两棵树合并成一棵树,新的直径一定也是原来四个端点中的两个。
这个题,考虑离线边长,之后的合并两个连通块时,新的直径考虑枚举四个点求他们的距离,发现他们的lca一定是这两个树中的点,所以用离线处理的信息就能解决。

点击查看代码
#include<bits/stdc++.h>
#define db double
using namespace std;
const int N=1e5+10;
int n,m,T,U[N],V[N],vis[N],deep[N],f[N][25];
int a[N],b[N],fa[N],cnt[N],px,c[N];
//a,b表示每个连通块的两个直径端点. 
vector<int>son[N];
inline int getf(int x){return fa[x]==x?x:fa[x]=getf(fa[x]);}
inline void dfs(int x,int fa)
{
	for(auto y:son[x])
	{
		if(y==fa) continue;
		vis[y]=1;
		deep[y]=deep[x]+1;
		f[y][0]=x;
		for(int i=1;i<=20;++i) f[y][i]=f[f[y][i-1]][i-1];
		dfs(y,x);
	}
}
inline int Lca(int a,int b)
{
	if(deep[a]>deep[b]) swap(a,b);
	for(int i=20;i>=0;--i) 
		if(deep[f[b][i]]>=deep[a]) b=f[b][i];
	if(a==b) return a;
	for(int i=20;i>=0;--i)
	{
		if(f[a][i]!=f[b][i])
			a=f[a][i],b=f[b][i];
	}
	return f[a][0];
}
inline int dis(int a,int b)
{
	return deep[a]+deep[b]-2*deep[Lca(a,b)];
}
inline void merge(int x,int y)
{
	fa[y]=x;
	int d[4]={a[x],b[x],a[y],b[y]};
	cnt[c[x]]--;cnt[c[y]]--;
	for(int i=0;i<4;++i)
		for(int j=i+1;j<4;++j)
		{
			int ds=dis(d[i],d[j]);
			if(ds>c[x])
			{
				a[x]=d[i];b[x]=d[j];
				c[x]=ds;
			}
		}
	cnt[c[x]]++;	
	px=max(px,c[x]);
}
inline void solve()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		vis[i]=0;deep[i]=0;
		son[i].clear();c[i]=0;
		for(int j=0;j<=20;++j) f[i][j]=0;
		cnt[i]=0;
	}
	cnt[0]=n;px=0;
	for(int i=1;i<=m;++i)
	{
		cin>>U[i]>>V[i];
		son[U[i]].push_back(V[i]);
		son[V[i]].push_back(U[i]);
	}
	for(int i=1;i<=n;++i) if(!vis[i]) 
	{
		vis[i]=1;
		dfs(i,0);	
	} 	
	for(int i=1;i<=n;++i) fa[i]=i,a[i]=i,b[i]=i;
	for(int i=1;i<=m;++i)
	{
		int t1=getf(U[i]),t2=getf(V[i]);//找到需要合并的两个连通块. 
		merge(t1,t2);
		int d[4]={-1,-1,-1,-1},o=0;
		for(int j=px;j>=max(0,px-5);--j)
		{
			for(int k=1;k<=cnt[j];++k)
			{
				d[++o]=j;
				if(o==3) break;
			}
			if(o==3)  break;
		}
		int ans=d[1];
		if(d[2]!=-1) ans=max(ans,(int)ceil(1.0*d[1]/2)+(int)ceil(1.0*d[2]/2)+1);
		if(d[3]!=-1) ans=max(ans,(int)ceil(1.0*d[2]/2)+(int)ceil(1.0*d[3]/2)+2);
		cout<<ans<<endl;
	} 
}
int main()
{
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>T;
	while(T--) solve();	
	return 0;
}

Photos

留坑待填...

posted @ 2022-08-19 15:37  逆天峰  阅读(38)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//