題目連接:BZOJ - 4033
php
使用樹形DP,用 f[i][j] 表示在以 i 爲根的子樹,有 j 個黑點的最大權值。ios
這個權值指的是,這個子樹內部的點對間距離的貢獻,以及 i 和 Father[i] 之間的邊對答案的貢獻(好比這條邊對黑點對距離和的貢獻就是子樹內部的黑點數 * 子樹外部的黑點數 * 這條邊的權值)。spa
而後DFS來求,枚舉 i 的每一個兒子 j,如今的 f[i][] 是包含了 [1, j-1] 子樹,而後兩重循環枚舉範圍是 [1, j - 1] 的子樹總 Size 和 j 的 Size,來更新 f[i][],這樣更新以後的 f[i][] 就是 [1, j] 子樹的答案了。blog
這樣的更新看起來是 O(n^3) 的,可是其實能夠看作枚舉了任意點對的LCA,因此複雜度實際上是 O(n^2) 的。get
#include <iostream> #include <cstdlib> #include <cstring> #include <cmath> #include <cstdio> #include <algorithm> using namespace std; typedef long long LL; inline int gmin(int a, int b) {return a < b ? a : b;} inline LL gmax(LL a, LL b) {return a > b ? a : b;} const int MaxN = 2000 + 5; int n, k; int Father[MaxN], Size[MaxN]; struct Edge { int v, w; Edge *Next; } E[MaxN * 2], *P = E, *Point[MaxN]; inline void AddEdge(int x, int y, int z) { ++P; P -> v = y; P -> w = z; P -> Next = Point[x]; Point[x] = P; } LL Ans; LL Temp[MaxN], f[MaxN][MaxN]; void Solve(int x, int Fa, int Num) { Size[x] = 1; for (Edge *j = Point[x]; j; j = j -> Next) { if (j -> v == Fa) continue; Solve(j -> v, x, j -> w); for (int i = 0; i <= gmin(Size[x], k); ++i) Temp[i] = f[x][i]; for (int p = 0; p <= gmin(Size[x], k); ++p) for (int q = 0; q <= gmin(Size[j -> v], k); ++q) f[x][p + q] = gmax(f[x][p + q], Temp[p] + f[j -> v][q]); Size[x] += Size[j -> v]; } for (int i = 0; i <= gmin(Size[x], k); ++i) f[x][i] += (LL)Num * (LL)(i * (k - i) + (Size[x] - i) * (n - Size[x] - k + i)); } int main() { scanf("%d%d", &n, &k); int x, y, z; for (int i = 1; i <= n - 1; ++i) { scanf("%d%d%d", &x, &y, &z); AddEdge(x, y, z); AddEdge(y, x, z); } Solve(1, 0, 0); cout << f[1][k] << endl; return 0; }