[CodeForces 1394D] Boboniu and Jianghu(树形 DP + 贪心) | 错题本

本文介绍了一种使用树形动态规划解决特定类型问题的方法,通过定义状态和转移方程来最小化节点对答案的贡献。文章以CodeForces1394D为例,详细解释了如何给树上的边定向,并通过排序和贪心策略找到最优解。

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

文章目录

题目

[CodeForces 1394D] Boboniu and Jianghu

分析

考虑树形 DP:对于当前点 u u u 和它的子节点 v v v,若 h u ≠ h v h_u \neq h_v hu=hv,那么这条边的方向就确定;若 h u = h v h_u = h_v hu=hv,我们需要给这条边定向。假设最终定完 u u u 所有邻接边的向后 u u u 的入度为 x x x,出度为 y y y,那么 u u u 对答案的贡献就是 t u × max ⁡ { x , y } t_u \times \max\{x, y\} tu×max{x,y}
于是可以定义状态: d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1] u u u 的父亲边朝向 u u u / / / 背向 u u u u u u 子树对答案的最小贡献。

树上给边定向的套路就是先给边钦定一个方向(例如我们钦定这些边都指向 v v v)并统计代价(代价为 d p [ v ] [ 0 ] dp[v][0] dp[v][0]),然后把改变该边方向的代价(即 d p [ v ] [ 1 ] − d p [ v ] [ 0 ] dp[v][1] - dp[v][0] dp[v][1]dp[v][0])放入数组中排序。从改变代价小的开始改变边的方向即可。

上述方法为什么是对的呢?因为对于一种改变了 p p p 条边方向的方案,其对“ u u u 对答案的贡献”的改变量是一定的(不考虑父亲边就是 t u × max ⁡ { x + p , y − p } t_u \times \max\{x + p, y - p\} tu×max{x+p,yp}),那么只需要贪心地取边改变量最小的 p p p 条即可。

代码

#include <bits/stdc++.h>

int Read() {
    int x = 0; char c = getchar();
    while (c < '0' || c > '9')
        c = getchar();
    while (c >= '0' && c <= '9')
        x = x * 10 + (c ^ 48), c = getchar();
    return x;
}

typedef long long LL;

const int MAXN = 200000;

int N;
int A[MAXN + 5], B[MAXN + 5];
std::vector<int> G[MAXN + 5];

LL Ans;
LL Dp[MAXN + 5][2];

void Dfs(int u, int f) {
    LL tot = 0;
    int out = 0, in = 0;
    std::vector<LL> num;
    for (int v: G[u]) {
        if (v != f) {
            Dfs(v, u);
            if (A[u] < A[v])
                tot += Dp[v][1], in++;
            else {
                tot += Dp[v][0], out++;
                if (A[u] == A[v])
                    num.push_aback(Dp[v][1] - Dp[v][0]);
            }
        }
    }
    std::sort(num.begin(), num.end());
    Dp[u][0] = tot + (LL)B[u] * std::max(in + (u != 1), out);
    Dp[u][1] = tot + (LL)B[u] * std::max(in, out + (u != 1));
    for (int val: num) {
        in++, out--, tot += val;
        Dp[u][0] = std::min(Dp[u][0], tot + (LL)B[u] * std::max(in + (u != 1), out));
        Dp[u][1] = std::min(Dp[u][1], tot + (LL)B[u] * std::max(in, out + (u != 1)));
    }
}

int main() {
    N = Read();
    for (int i = 1; i <= N; i++)
        B[i] = Read();
    for (int i = 1; i <= N; i++)
        A[i] = Read();
    for (int i = 1; i < N; i++) {
        int u = Read(), v = Read();
        G[u].push_back(v);
        G[v].push_back(u);
    }
    Dfs(1, 0);
    printf("%lld", Dp[1][0]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值