M - Kill the tree 计蒜客 - 42552(2019icpc徐州/树的重心/树形dp)

vj地址
题目大意:找到每一颗子树的重心
思路
树的重心的性质:

  1. 树的重心如果不唯一,则至多有两个,且这两个重心相邻

  2. 通过连接一条端点分别在两个树的边,来将两个树合并成一个,那么新的重心肯定是在原来这两个树的重心的路径上(两颗树合并重心的转移)
    (应该不会有人不知道树的重心的定义吧)

根据这个每次合并两个树的时候,找到新树的重心,就在两个重心的路径上,该路径一定会经过根节点,所以我们转移重心的时候最多转移到根节点就好了。

具体细节代码有注释

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f3f3f3f
#define FILL(a,b) (memset(a,b,sizeof(a)))
#define re register
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(a) ((a)&-(a))
#define ios std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
#define fi first
#define se second

using namespace std;
typedef long long  ll;
typedef unsigned long long  ull;
typedef pair<int,int > pii;
int dx[4]= {-1,1,0,0},dy[4]= {0,0,1,-1};
const ll mod=2520;
const int N=2e5+10;
int n;
int son[N],d[N],vis[N],p[N],dp[N];
vector <int> g[200010];
vector<int> ans[N];
//转移,只有满足son[x]<son[u]-son[x]的才能够转移,x==u时就不能够转移了,两棵树转移到最后的时候,
//深度更大的那个必然是重心,不是重心的会一直走到树根点,如果有两个重心那一定是重心的父节点
void up(int u,int x,int y){//转移向上爬
    while(x!=u&&son[x]<son[u]-son[x]){
        x=p[x];
    }
    while(y!=u&&son[y]<son[u]-son[y]){
        y=p[y];
    }
    if(d[x]>d[y]) dp[u]=x;
    else dp[u]=y;
}
void dfs(int u,int f){//树形dp
    vis[u]=1;dp[u]=u;
    son[u]=1;p[u]=f;d[u]=d[f]+1;
    for(int v:g[u]){
        if(v==f||vis[v]) continue;
        dfs(v,u);
        son[u]+=son[v];
        up(u,dp[u],dp[v]);//合并两颗树找到重心
    }
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++){
        if(son[dp[i]]==son[i]-son[dp[i]])//判断父节点是不是重心
           cout<<min(dp[i],p[dp[i]])<<" "<<max(dp[i],p[dp[i]])<<endl;
        else cout<<dp[i]<<endl;
    }
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值