医院设置

树的重心的定义:

树若以某点为根,使得该树最大子树的结点数最小,那么这个点则为该树的重心,一棵树可能有多个重心。

树的重心的性质:

1、树上所有的点到树的重心的距离之和是最短的,如果有多个重心,那么总距离相等。

2、插入或删除一个点,树的重心的位置最多移动一个单位。

3、若添加一条边连接2棵树,那么新树的重心一定在原来两棵树的重心的路径上。

当然,这题我们只需要用到第一条性质。

怎么求树的重心:

定义几个数组:f[u]f[u]表示以u为根的总距离,size[u]size[u]表示以u为根的子树的大小(结点数,此题每个点要乘以权值,下文结点数均指此)。

显然,ans=min(f[i],1<=i<=n)ans=min(f[i],1<=i<=n)
首先我们任意以一个点为根dfs一遍,求出以该点为根的总距离。方便起见,我们就以1为根。

接下来就是转移,对于每个u能达到的点v,有:
f[v]=f[u]+size[1]-size[v]-size[v]f[v]=f[u]+size[1]−size[v]−size[v]
怎么来的呢?试想,当根从u变为v的时候,v的子树的所有节点原本的距离要到uu,现在只要到vv了,每个结点的距离都减少1,那么总距离就减少size[v]size[v],同时,以v为根的子树以外的所有节点,原本只要到uu就行了,现在要到vv,每个节点的路程都增加了1,总路程就增加了size[1]-size[v]size[1]−size[v],其中size[1]size[1]就是我们预处理出来的整棵树的大小,减去size[v]size[v]就是除以v为根的子树以外的结点数。

最后取最小值,得解。时间复杂度O(n)O(n)
附上代码:

#include <cstdio>
#define rep(i, m, n) for(register int i = m; i <= n; ++i)
#define INF 2147483647
#define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define Close fclose(stdin);fclose(stdout);
using namespace std;
inline int read(){
    int s = 0, w = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }
    return s * w;
}
const int MAXN = 10010;
struct Edge{
    int next, to;
}e[MAXN << 1];
int head[MAXN], num, w[MAXN], n, size[MAXN];
long long ans = INF, f[MAXN];
inline void Add(int from, int to){
    e[++num].to = to;
    e[num].next = head[from];
    head[from] = num;
}
void dfs(int u, int fa, int dep){ //预处理f[1]和size
    size[u] = w[u];
    for(int i = head[u]; i; i = e[i].next){
       if(e[i].to != fa)
         dfs(e[i].to, u, dep + 1), size[u] += size[e[i].to];
    }
    f[1] += w[u] * dep;
}
void dp(int u, int fa){  //转移
    for(int i = head[u]; i; i = e[i].next)
       if(e[i].to != fa)
         f[e[i].to] = f[u] + size[1] - size[e[i].to] * 2, dp(e[i].to, u);
    ans = min(ans, f[u]); //取最小值
}
int a, b;
int main(){
    //Open("hospital");
    ans *= ans;
    n = read();
    rep(i, 1, n){
       w[i] = read();
       a = read(); b = read();
       if(a) Add(i, a), Add(a, i);
       if(b) Add(i, b), Add(b, i);
    }
    dfs(1, 0, 0);
    dp(1, 0);
    printf("%lld\n", ans);
    //Close;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值