1406C - Link Cut Centroids(翻译自官方题解)

其实这题我挺不喜欢的,大部分人都是靠才结论过的吧??

树的重心:删掉这个节点,形成的最大连通块最小

所以可以先 d f s dfs dfs一遍求出重心

若重心只有一个,随便删掉一条边再连回去

否则最多有两个重心,且相邻

Ⅰ . 为 什 么 只 有 两 个 重 心 \color{Red}Ⅰ.为什么只有两个重心 .

很抱歉,在网上找了一圈没看到有人证过,然后我自己也很菜~

我在纸上画了一下,似乎如果有 3 3 3个重心必须成环形成对称的连通块大小才行

树是不能成环的

Ⅱ . 为 什 么 这 两 个 重 心 相 邻 ( 一 下 翻 译 自 官 方 题 解 ) \color{Red}Ⅱ.为什么这两个重心相邻(一下翻译自官方题解) .()

x , y x,y x,y是重心

如果 x , y x,y x,y的路径上有其他节点,那么那些节点更可能成为重心,而不是 x , y x,y x,y

x x x y y y的父亲,可以知道 y y y的子树大小是 n 2 \frac{n}{2} 2n(这个在纸上画一下)

这样我们从 y y y的子树中剪下一个叶子,连到 x x x上面去

这样 x x x就变成了唯一的重心,此时删掉 y y y形成的最大连通块变成 n 2 + 1 \frac{n}{2}+1 2n+1

而删掉 x x x形成的最大连通块仍是 n 2 \frac{n}{2} 2n

代码中,我是直接把 y y y的一个儿子减下来连到 x x x上面去的

#include 
using namespace std;
const int maxn=4e5+10;
struct edge{
     
	int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){
     	d[++cnt]=(edge){
     v,head[u]},head[u]=cnt; }
int siz[maxn],minn=1e9,xin[maxn],n,t,p;
void dfs(int u,int fa)
{
     
	siz[u]=1;
	for(int i=head[u];i;i=d[i].nxt )
	{
     
		int v=d[i].to;
		if( v==fa )	continue;
		dfs(v,u);
		siz[u]+=siz[v];
		xin[u]=max(xin[u],siz[v]);
	}
	xin[u]=max(xin[u],(n-siz[u]) );
	minn=min( minn,xin[u] );//找删掉节点后最大连通块最小的数值 
}
int main()
{
     
	cin >> t;
	while( t-- )
	{
     
		cin >> n;
		int l,r; minn=1e9;
		for(int i=1;i<n;i++)
		{
     
			cin >> l >> r;
			add(l,r); add(r,l);
		}
		dfs(1,0);
		int x=0,y=0;
		for(int i=1;i<=n;i++)
			if( xin[i]==minn )
			{
     
				if( x )	y=i;
				else	x=i;
			}
		if( x&&!y )
		{
     
			cout << l << " " << r << endl;
			cout << l << " " << r << endl;
		}
		else//2个重心 
		{
     
			if( siz[x]<siz[y] )	swap(x,y);
			int oo=0;
			for(int i=head[y];i;i=d[i].nxt )
				if( d[i].to!=x&&d[i].to!=y )	oo=d[i].to;
			cout << oo << " " << y << "\n";
			cout << oo << " " << x << "\n";
		}
		cnt=1;
		for(int i=0;i<=n;i++)	head[i]=xin[i]=0;
	}
}

你可能感兴趣的:(div题解)