Codeforces Round #646 (Div. 2) E. Tree Shuffling(树上dp)

题目链接:https://codeforces.com/contest/1363/problem/E

题意

有一棵 $n$ 个结点,根为结点 $1$ 的树,每个结点有一个选取代价 $a_i$,当前 $b_i$,目标数字 $c_i$ 。每次可以选择以一个结点为根节点的子树中的 $k$ 个结点交换它们的 $b_i$,总代价为 $k imes a_{root}$ ,判断能否把所有结点都变为目标数字以及最小代价。

题解

因为每棵子树都可以被包含进更大的子树中,所以对于每棵子树的根节点,它的最小选取代价可以取它自身和所有可能的父节点的最小值。

计算每棵子树中需要交换的 $0$ 和 $1$ 的个数,用根节点的最小选取代价交换后,多余的 $0$ 或 $1$ 累加至父节点所在的子树即可。

代码

#include <bits/stdc++.h>
using ll = long long;
using namespace std;
const int N = 2e5 + 10;

vector<int> G[N];
int a[N], b[N], c[N];
int cnt[N][2];
ll ans;

void dfs(int u, int pre) {
    if (pre != 0) a[u] = min(a[u], a[pre]);
    for (auto v : G[u]) {
        if (v != pre) {
            dfs(v, u);
            cnt[u][0] += cnt[v][0];
            cnt[u][1] += cnt[v][1];
        }
    }
    if (b[u] != c[u]) cnt[u][b[u]]++;
    int mi = min(cnt[u][0], cnt[u][1]);
    ans += 2LL * mi * a[u];
    cnt[u][0] -= mi;
    cnt[u][1] -= mi;
}

int main() {
    int n; cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i] >> b[i] >> c[i];
    for (int i = 0; i < n - 1; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1, 0);
    cout << ((cnt[1][0] or cnt[1][1]) ? -1 : ans) << "
";
}

参考博客:https://www.cnblogs.com/axiomofchoice/p/13022886.html

原文地址:https://www.cnblogs.com/Kanoon/p/13024219.html