Codeforces Round #670 (Div. 2) C(树的重心)

题意:

给你一棵树,你可以删除一条边,添加一条边(可以删除添加同一条),使得这棵树的重心唯一,输出删除的边,和添加的边。

前置知识:

树的重心及性质
  • 首先要知道什么是树的重心,树的重心就是:树中的一个节点,他满足,删除这个节点,剩下若干个连通块,这若干个连通块中最大的那个最小。
  • 重心一些基本性质有:
    • 删除重心后所得的所有子树,节点数不超过原树的1/2,一棵树最多有两个重心;
    • 树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等;
    • 两个树通过一条边合并,新的重心在原树两个重心的路径上;
    • 树删除或添加一个叶子节点,重心最多只移动一条边;
    • 一棵树最多有两个重心,且相邻。
重心的求法:
  • 把无根树化成有根树,求出以每个节点为根节点,子树的大小记作 s z [ i ] sz[i] sz[i],那么每个节点作为根节点最大的子树的大小就是 m a x ( s z [ i ] − 1 , s z [ f a ] − s z [ i ] ) max(sz[i]-1,sz[fa]-sz[i]) max(sz[i]1,sz[fa]sz[i])

题解:

求出重心,如果有两个,那么这两个必然在同一条边上(由重心性质可知),我把其中重心的子分支挪到另一个子分支上,就可以了

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
vector<int>G[100005];
int f[100005],sz[100005],mx[100005];
void dfs(int u,int fa) {
    
    
	f[u] = fa;
	sz[u] = 1;
	for(auto v : G[u]) {
    
    
		if(v != fa) {
    
    
			dfs(v,u);
			sz[u] += sz[v];
			mx[u] = max(mx[u],sz[v]);
		}
	}
}
int main()
{
    
    
	int T;
	cin >> T;
	while(T--)
	{
    
    
		int n;
		cin >> n;
		
		for(int i=1;i<n;i++)
		{
    
    
			int u,v;
			scanf("%d%d",&u,&v);
			G[u].pb(v);
			G[v].pb(u);
		}
		dfs(1,0);
		int inf = 1e9,core;
		for(int i=1;i<=n;i++) {
    
    
			mx[i] = max(mx[i],sz[1]-sz[i]);
			
			if(mx[i] < inf) {
    
    
				inf = mx[i];
				core = i;
			}
		}
		
		int flag = 0;
		vector<int>ans;
		for(int i=1;i<=n;i++)
		{
    
    
			if(mx[i] == inf)ans.pb(i);
		}
		
		if(ans.size() == 1) {
    
    
			cout<<ans[0]<<' '<<G[ans[0]][0]<<'\n';
			cout<<G[ans[0]][0]<<' '<<ans[0]<<'\n';
		} else {
    
    
			int tmp;
			for(auto v : G[ans[0]]) {
    
    
				if(v != ans[1])
				{
    
    
					tmp = v;
					break;
				}
			}
			cout<<ans[0]<<' '<<tmp<<'\n';
			cout<<tmp<<' '<<ans[1]<<'\n';
		}
		
		
		for(int i=1;i<=n;i++)
		{
    
    
			sz[i] = 0;
			mx[i] = 0;
			G[i].clear();
			f[i] = 0;
		}
	}
 } 

猜你喜欢

转载自blog.csdn.net/weixin_44499508/article/details/108566477