Pre
想不出来,不得不说这套路还不错。
调试代码到了最后发现有一个地方 (u) 写成了 (to[i])
Solution
考虑每一条边的贡献。
(w=w_i*cntblack_{left}*cntblack_{right}+w_i*cntwhite_{left}*cntwhite_{right})
以这个等式进行 (DP)
最终计算答案。
Code
#include <cstdio>
#include <cstring>
#include <limits.h>
#define ll long long
using namespace std;
inline ll min (ll u, ll v) { return u > v ? v : u;}
inline ll max (ll u, ll v) { return u > v ? u : v;}
const int N = 2000 + 5;
int n, k;
int fr[N << 1], to[N << 1], h[N], tot, sz[N];
ll val[N << 1], dp[N][N];
inline void dfs (int u, int f) {
memset (dp[u], -1, sizeof dp[u]);
dp[u][0] = dp[u][1] = 0;
sz[u] = 1;
for (int i = h[u]; i; i = fr[i]) {
if (to[i] == f) continue;
dfs (to[i], u);
sz[u] += sz[to[i]];
}
for (int i = h[u]; i; i = fr[i]) {
if (to[i] == f) continue;
for (int c = min (k, sz[u]); c >= 0; --c) {
for (int j = 0; j <= min (c, sz[to[i]]); ++j) {
if (~dp[u][c - j])
dp[u][c] = max (dp[u][c], dp[u][c - j] + dp[to[i]][j] + val[i] * j * (k - j) + val[i] * (sz[to[i]] - j) * (n - k + j - sz[to[i]]));
}
}
}
}
inline void add (int u, int v, ll w) {
tot++;
fr[tot] = h[u];
to[tot] = v;
h[u] = tot;
val[tot] = w;
}
int main () {
#ifdef chitongz
freopen ("x.in", "r", stdin);
#endif
scanf ("%d%d", &n, &k);
for (int i = 1; i < n; ++i) {
int x, y; ll z;
scanf ("%d%d%lld", &x, &y, &z);
add (x, y, z); add (y, x, z);
}
dfs (1, 0);
printf ("%lld
", dp[1][k]);
return 0;
}
Conclusion
尝试换一个方向思考。
Update
有人询问代码中
if (~dp[u][c - j])
的作用。
也就是为什么没有初始化的点不能使用,可能想到 (-1) 不会对答案造成贡献。
但是假设根节点 (rt) 有子树 (p_1,p_2,cdots) ,并且按顺序更新。
由于是先把 (sz[u]) 求出来再进行更新,所以在更新这个子树的时候,前面的子树的大小是严格小于 (sz[u]) 的,而转移当中取得 (c) 是 (min(k,sz[u])) ,也就是可以取到 (sz[u]) ,而一旦取到就会出现我想要在原来的子树安排的黑色节点数量大于它的数量上限,也就是(sz[p_1]+sz[p_2]+cdots)。这种情况下由于我的白色节点个数较多,答案可能更优,甚至远大于 (-1) ,所以依然会更新根节点的 (DP) 值。也就是出现了,比如,某一个大小为 (5) 的子树,有两个子树,大小分别为 (1,3) ,某一个 (DP) 值要求在这个大小为 (5) 的子树安排下 (3) 个黑色节点,表面上看是可行的,但是 (DP) 的结果却是在大小为 (1) 的子树安排 (2) 个黑色节点,在另一个子树安排 (1) 个黑色节点,这样才能达到 (rt) 节点的最优。但是这样是不可能的,其原因就在于大小为 (3) 的子树在更新答案的使用非法使用了前面的子树的 (DP) 值,也就是 (dp[rt][2]) ,安排 (2) 个黑色节点的 (DP) 值。