# 【树形DP】【P3177】[HAOI2015] 树上染色

## Limitations

$0 \leq k \leq n \leq 2000$

## Solution

func dfs(u):
size[u] <- 1
for v in clild[u] do
dfs(v)
for i = 1 : size[u] do
for j = 1: size[v] do
do sth
end
end
size[u] <- size[u] + size[v]
end
end func

$f_{u,i,j} = \max_{h = 0}^{j} f_{u, i-1, h} + f_{v, size[v], j-h} + val$

$[x \times (k-x) + (size_v - x) \times (n - k - size_v + x)] \times e_w$

$f_{u,j} = \max_{h=0}^j f_{u, h} + f_{v, j-h} + val$

func dfs(u):
size[u] <- 1
for v in clild[u] do
dfs(v)
size[u] <- size[u] + size[v]
for sum = 1 : size[u] do
for i = max(0, sum - size[v]) : size[v] do
do sth
end
end
end
end func

## Code

#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn = 2003;

int n, K, dK;
int sz[maxn];
ll frog[maxn][maxn];

struct Edge {
int v, w;
Edge *nxt;

Edge(const int _v, const int _w, Edge *h) : v(_v), w(_w), nxt(h) {}
};
Edge *hd[maxn];

void dfs(const int u, const int fa);

int main() {
freopen("1.in", "r", stdin);
qr(n); qr(K); dK = n - K;
for (int i = 1, u, v, w; i < n; ++i) {
u = v = w = 0; qr(u); qr(v); qr(w);
hd[u] = new Edge(v, w, hd[u]);
hd[v] = new Edge(u, w, hd[v]);
}
dfs(1, 0);
qw(frog[1][K], '\n', true);
return 0;
}

void dfs(const int u, const int fa) {
sz[u] = 1;
memset(frog[u] + 2, -1, 16008);
for (auto e = hd[u]; e; e = e->nxt) if (e->v != fa) {
int v = e->v; dfs(v, u); sz[u] += sz[v];
for (int i = std::min(sz[u], K); ~i; --i) {
for (int  j = i, lim = std::max(0, i - sz[v]); j >= lim; --j) if (~frog[u][j]) {
int k = i - j; if (frog[v][k] == -1) continue;
ll val = (1ll * k * (K - k) + 1ll * (sz[v] - k) * (dK - sz[v] + k)) * e->w;
frog[u][i] = std::max(frog[u][i], frog[u][j] + frog[v][k] + val);
}
}
}
}

## Summary

1、上面那种神奇的枚举方式遍历整棵树的时间复杂度是 $O(n^2)$

2、当问题不满足最优子结构时，可以考虑DP每个子问题对答案的贡献。

3、很多时候刷表法难以滚动数组，需要转化成填表法

## appreciation

posted @ 2019-08-05 15:11  一扶苏一  阅读(261)  评论(0编辑  收藏  举报