Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost

本文解析了CodeForces竞赛中题目F的解决方案,通过树形DP算法实现快速求解。介绍了如何预处理子树权值之和并利用转移方程高效计算不同节点作为根节点时的最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://codeforces.com/contest/1092/problem/F

解题心得:题意很简答就不多说了。仔细观察可以发现如果知道了一个点作为 V V V点得到的答案,那么当转移到它的儿子节点时可以直接 O ( 1 ) O(1) O(1)得到。具体转移是这样的,先随便选择一个点作为根节点,记为 r o o t root root。预处理出从根节点开始每个节点为子树时根节点时的权值和记为 s u m [ i ] sum[i] sum[i]。转移方程也很简单,假设已经得到了 V V V点的答案,转移到 V V V的儿子 u u u那么可以这样转移 d p [ u ] = d p [ V ] + ( s u m [ r o o t ] − s u m [ u ] ) − s u m [ u ] dp[u] = dp[V] + (sum[root] - sum[u]) - sum[u] dp[u]=dp[V]+(sum[root]sum[u])sum[u]。其实就是一个很简单的树上dp。



#include <bits/stdc++.h>
using namespace std;
typedef complex<double> cp;
typedef long long ll;
const ll maxn = 2e5+100;
const double pi = acos(-1);

ll n, num[maxn], dp[maxn], sum[maxn];

vector <int> ve[maxn];

void init() {
    scanf("%lld", &n);
    for(int i=1;i<=n;i++) scanf("%lld", &num[i]);
    for(int i=1;i<n;i++) {
        int a, b; scanf("%d%d", &a, &b);
        ve[a].push_back(b);
        ve[b].push_back(a);
    }
}

ll dfs1(int pre, int now, ll deep) {
    ll Sum = num[now]*deep;
    for(int i=0;i<ve[now].size();i++) {
        int v = ve[now][i];
        if(v == pre) continue;
        Sum += dfs1(now, v, deep+1);
    }

    return Sum;
}

ll dfs2(int pre, int now) {
    sum[now] = num[now];
    for(int i=0;i<ve[now].size();i++) {
        int v = ve[now][i];
        if(v == pre) continue;
        sum[now] += dfs2(now, v);
    }
    return sum[now];
}

void DP(int pre, int now) {
    if(pre != -1) {
        dp[now] = dp[pre] + (sum[1] - sum[now]) - sum[now];
    }
    for(int i=0;i<ve[now].size();i++) {
        int v = ve[now][i];
        if(v == pre) continue;
        DP(now, v);
    }
}

int main() {
    //    freopen("1.in.txt", "r", stdin);
    init();
    dp[1] = dfs1(-1, 1, 0);//获得root的答案
    dfs2(-1, 1);//预处理出所有子树的权值和

    DP(-1, 1);//递归转移
    ll Max = -1;
    for(int i=1;i<=n;i++) Max = max(Max, dp[i]);
    printf("%lld\n", Max);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值