CF1092E Solution

题目链接

题解

下文将“以该节点为根时树的最大深度最小”的节点称为“最小节点”,并将“以最小节点为根时树的最大深度”称为“最小深度”。

题目给出的是无根树森林,易得对于每一棵树来说,使“最小节点”与其他树连接最优。而最终方案则是将所有树与“最小深度”最大(或第二大)的树建边,证明:

设树(A,B,C)的“最小深度”分别为(x,y,z(x<y<z)),若将(y,z)分别与(z)树的“最小节点”连边,则直径最小为(min(y+z+2,x+y+1,x+z+1)=y+z+2);而将(x,y)分别与(x)树的“最小节点”连边,则直径最小为(min(x+z+2,x+y+1,y+z+1)=y+z+1)(x,z)(y)同理。

建树后求树的直径即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int fst[N],nxt[2*N],v[2*N],cnt;
int siz[N],rt[N],dp[N],t[N],pos;
int d[N],qwq[N],qaq[N],tot,ans;
//siz[i]:编号为i树的"最小深度",rt[i]:编号为i树的"最小节点",dp[i]:i号节点为根时它所在树的最大深度
//t[i]:编号为i结点所在树的编号,qwq/qaq:记录答案的建边
void add(int a,int b)
{
	v[++cnt]=b;
	nxt[cnt]=fst[a]; fst[a]=cnt;
}
void dfs1(int x)
{
	t[x]=pos;
	for(int i=fst[x];i;i=nxt[i])
	{
		int y=v[i];
		if(!t[y]) dfs1(y); 
	}
}
int dfs2(int x,int fa)
{
	int sum=0;
	for(int i=fst[x];i;i=nxt[i])
	{
		int y=v[i];
		if(y==fa) continue;
		sum=max(sum,dfs2(y,x));
	}
	return sum+1;
}
void dfs3(int x,int fa)
{
	for(int i=fst[x];i;i=nxt[i])
	{
		int y=v[i];
		if(y==fa) continue;
		dfs3(y,x);
		ans=max(ans,d[x]+d[y]+1);
		d[x]=max(d[x],d[y]+1);
	}
}
int main()
{
	int n,m,x,y;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) 
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x); 
	}
    //计算t数组
	for(int i=1;i<=n;i++)
		if(!t[i]) {pos++; dfs1(i);}
    //计算dp数组
	for(int i=1;i<=n;i++) dp[i]=dfs2(i,0)-1;
    //计算siz数组
	memset(siz,0x3f,sizeof(siz));
	for(int i=1;i<=n;i++)
		if(dp[i]<siz[t[i]]) {siz[t[i]]=dp[i]; rt[t[i]]=i;}
    //求出最小深度最大树的最小节点
	int maxi=-1,maxn;
	for(int i=1;i<=pos;i++)
		if(siz[i]>maxi) {maxi=siz[i]; maxn=i;}
    //记录答案+建边
	for(int i=1;i<=pos;i++)
	{
		if(i!=maxn) 
		{
			qwq[++tot]=rt[maxn]; qaq[tot]=rt[i];
			add(rt[maxn],rt[i]); add(rt[i],rt[maxn]);
		}
	}
    //求树的直径
	dfs3(1,0);
	printf("%d
",ans);
	for(int i=1;i<=tot;i++) printf("%d %d
",qwq[i],qaq[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14321634.html