[HAOI2015] 树上染色

[HAOI2015] 树上染色

树形 \(DP\) , 一道有点恶心的树上背包.

设状态 \(f[x][i]\) 表示以 \(x\) 为根节点的子树中选 \(i\) 个节点染黑的最大价值.

考虑转移, 枚举以 \(x\) 为根节点的子树中选 \(j\) 个黑点, \(x\) 的子节点 \(y\) 选 \(k\) 个黑点, 那么 \(dis(i, j)\) 就被计算了 \(k * (m - k) + (son[y] - k) * (n - son[y] - m + k)\) 次, 也就是 \(子树内的黑点数 * 子树外的黑点数 + 子树内的白点数 * 子树外的白点数\) , 那么 \(f[x][j] = \max(f[x][j], f[y][k] + f[x][j - k] + (k * (m - k) + (son[y] - k) * (n - son[y] - m + k)) * z\) .

为什么说这个题恶心呢, 因为它有一个极端数据...也就是 \(Subtask\ \#1\) ...这个点是链, 跑得巨慢, 使得我们的代码效率远达不到树上背包在随机数据上的效率(所以要开 \(O2\) bushi) .

\(code:\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
const int N = 2005, M = 4005;
int n, m, son[N];
int tot, to[M], dis[M], nxt[M], head[N];
ll f[N][N];
void add(int u, int v, int w) {
    to[++tot] = v, dis[tot] = w, nxt[tot] = head[u], head[u] = tot;
}
void dfs(int x, int fa) {
    son[x] = 1; f[x][0] = f[x][1] = 0;
    for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i], z = dis[i];
        if (y == fa) continue;
        dfs(y, x);
        son[x] += son[y];
        for (int j = min(son[x], m); j >= 0; j--) {
            if (f[x][j] != -1) f[x][j] += f[y][0] + (ll)son[y] * (n - son[y] - m) * z;
            for (int k = min(son[y], j); k > 0; k--) {
                if (f[x][j - k] == -1) continue;
                ll num = (ll)k * (m - k) + (ll)(son[y] - k) * (n - son[y] - m + k);
                f[x][j] = max(f[x][j], f[y][k] + f[x][j - k] + num * z);
            }
        }
    }
}
int main() {
    memset(f, -1, sizeof(f));
    n = read(), m = read();
    for (int i = 1; i < n; i++) {
        int u = read(), v = read(), w = read();
        add(u, v, w); add(v, u, w);
    }
    dfs(1, 0);
    printf("%lld", f[1][m]);
    return 0;
}
上一篇:Java 多态学习总结


下一篇:P3469 [POI2008]BLO-Blockade 题解