2019 徐州 M. Kill the tree(树的重心的性质,子树合并)

2019 徐州 M. Kill the tree(树的重心的性质,子树合并)

这题写得好爽

题意

求一棵有根树中,以每个节点为根的子树的所有重心

思路

前置芝士

树的重心

性质:

1. 最大的子树最小
2. 找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡
3. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样,则这两个点都是重心(即重心可以有两个)
4. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上
5. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置
6. 一棵树最多有两个重心,且相邻。

本题:
什么条件下重心可以往上爬: 设当前重心为u,根节点为rt,sz[u] 是 u 结点的为根节点的子树的结点个数。若u往上爬,u的所有子节点到u的距离会加一,其它点到u的距离会减一,根据题目给的计算式,这个该变量必须是负值才可以往上爬:那么得到一个条件是:sz[rt] - sz[u] > sz[u],满足这个条件 u 就可以向上爬,直到 u == rt 或者 条件不满足

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e5+5;
int n,dep[maxn],fa[maxn],size[maxn],son[maxn],ans[maxn];
vector<int>gap[maxn];
void dfs(int now,int pre)
{
	dep[now] = dep[pre]+1;
	fa[now] = pre;
	size[now] = 1;
	son[now] = 0;
	for(auto &x:gap[now]){
		if(x==pre)continue;
		dfs(x,now);
		size[now] += size[x];
		if(size[x] > size[son[now]])son[now] = x;
	}
}
void solve(int now,int pre){
	for(int i=0;i<gap[now].size();i++){
		if(gap[now][i]!=pre)solve(gap[now][i],now);
	} 
	ans[now]=now;
	if(!son[now])return;
	int x=ans[son[now]];
	while(x!=fa[now]&&size[now]-size[x]>size[x])x=fa[x];
	ans[now]=x;
}
int main(){
	scanf("%d",&n);
	for(int i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);
		gap[y].push_back(x);
		gap[x].push_back(y);
	}
	dfs(1,0);
	solve(1,0);
	for(int i=1;i<=n;i++){
		int t=ans[i],f=fa[t];
		if(f&&size[i]-size[t]==size[t]){
			if(f<t)swap(t,f);
			printf("%d %d\n",t,f);
		}
		else printf("%d\n",t);
	}
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值