ICPC 2019 徐州 M - Kill the tree

M - Kill the tree

题目传送门

题目大意:给你一棵树,以 1 1 1为根,你需要求出以每个节点作为根的子树的重心,按照升序输出。

题解:首先我们需要知道树的重心的一些性质。

  1. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个重心,他们的距离和一样。
  2. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
  3. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
  4. 一棵树最多有两个重心,且相邻。
    这道题目主要用到了1、2、4三条性质。而且在一顿推断猛如虎,你还可以得到多颗子树和一个新的根节点合并时形成的新的树的重心在最重子树(节点最多的子树)的重心到根的链上。其实我还不会严格证明,反正就是对的 。那么主要就是写一个dfs在当前树的子树的重心都被找到的时候,先找到最重的子树,一步步向父节点移。具体看代码。
vector<int> vi[maxn],ans[maxn];
int father[maxn],size[maxn];//father记录父节点,size记录以当前节点为根的子树的节点数
void dfs(int u,int fa){
	father[u]=fa;
	size[u]=1;
	int ma=0,index=0;
	for(int i=0;i<vi[u].size();i++){
		if(vi[u][i]==fa) continue;
		dfs(vi[u][i],u);
		size[u]+=size[vi[u][i]];//加上每一个子树的size
		if(size[vi[u][i]]>ma)//找到最大的子树
		{
			ma=size[vi[u][i]];
			index=vi[u][i];
		}
	}
	if(!index){//没有子树说明递归到了叶子节点,当前节点就是重心
		ans[u].pb(u);
		return ;
	}
	int temp=ans[index][0];//初始化为最大子树的重心
	int flag=1;
	while(size[temp]<=size[u]-size[temp]){//判断能否向父节点移动
		if(size[temp]==size[u]-size[temp])//移动后距离和不变,说明这两个都是重心 
		{
			ans[u].pb(temp);
			ans[u].pb(father[temp]);
			sort(ans[u].begin(),ans[u].end());
			flag=0;
			break;
		}
		temp=father[temp]; //更新当前重心
	}
	if(flag)
	ans[u].pb(temp);
}

主要复杂的也就是这个dfs函数了,其它代码就不贴了。

你可能感兴趣的:(ICPC 2019 徐州 M - Kill the tree)