[ZJOI2007]时态同步 树形DP

题意:给定一棵N个节点的无根树,每条边都有一个权值V,选取其中一个点作为关键点,你可以任意增加某条边的权值,求使得从关键点出发,到任意一个叶子节点的距离都相同所需要增加的权值和。

 

数据范围:

对于40%的数据,N ≤ 1000

对于100%的数据,N ≤ 500000,V ≤ 1000000

 -------------------------------------分割线------------------------------------

题解:一道比较显然的树形DP,不难想到我们把其他的链都向最长链看齐。

扫描一遍树,自底向上处理出当前节点的出边数量num,以及当前节点到叶节点的距离和sum。

算出最远的距离激发器的叶子节点距离maxn[x],然后对于其他叶子节点,所需要增加的长度显然即为 maxn[x] * num - sum。

累加答案即可。

#include<bits/stdc++.h>

#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)

using namespace std;

typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, a[N], root; 
int head[N], cnt = 0;
ll maxn[N], f[N];
struct node{ ll to, next, v; } e[N];
inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}
    while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
    return x*f;
}
void add(int x, int y, int z){
    e[++cnt].to = y; e[cnt].v = z;
    e[cnt].next = head[x]; head[x] = cnt;
}
void init(){
    n = read(); root = read();
    rep(i, 1, n-1) {
        int xx, yy, zz;
        xx = read(); yy = read(); zz = read();
        add(xx, yy, zz);
        add(yy, xx, zz);
    }  
}
void dfs(int x, int fa){
    f[x] = 0, maxn[x] = 0;
    ll sum = 0, num = 0;
    for(int i = head[x]; i; i = e[i].next){
        int y = e[i].to;
        if(y == fa) continue;
        dfs(y, x); num ++;
        f[x] += f[y];
        maxn[x] = max(maxn[y] + e[i].v, maxn[x]);
        sum += (ll)maxn[y] + e[i].v; 
    }
    f[x] += (ll)maxn[x] * num - sum;
}
void work(){
    dfs(root, 0);
    printf("%lld\n", f[root]);
}
int main(){
    init();
    work();
    return 0;
}
View Code

 

posted @ 2019-09-24 10:47  smilke  阅读(125)  评论(0编辑  收藏  举报