[HAOI2015]树上染色
两点之间贡献和的问题转化成边的贡献
已经深搜过的点的个数为son[u] 回溯得到的另外一个子树的个数为son[v]
然后每一条边的贡献分别由黑点和白点组成
设遍历到的树边靠近子树的一端黑点为x, 即 黑内为x 黑外 为 K - x 那么黑点的贡献为x * (son[v] - x)
白内为 son[v] - x 白外为(n - son[v] - ( son[v] - x ) ) 白点的贡献为 前面两者相乘
#include <cstdio> #include <cstring> #include <queue> #include <vector> #include <algorithm> typedef long long ll; using namespace std; const ll inf = 0x3f3f3f3f; using namespace std; const ll maxn = 2e3+10; const ll maxm = 2e3+10; struct node { ll v, w; ll nxt; }edge[maxm * 2]; ll head[maxn], tot; void Insert(ll u, ll v, ll w) { edge[++tot].v = v; edge[tot].w = w; edge[tot].nxt = head[u]; head[u] = tot; } ll son[maxn]; ll n, m, K; ll dp[maxn][maxn]; ll f(ll x, int num, int v) { return x * num * (K - num) + x * (n - son[v] - (K - num)) * (son[v] - num); } void dfs(ll u) { son[u] = 1; for (ll i = head[u]; i; i = edge[i].nxt) { int v = edge[i].v; if (son[v]) {continue;} dfs(v); for (ll j = min(son[u], K); j >= 0; j--) { for (int k = min(K, son[v]); k >= 0; k--) dp[u][j + k] = max(dp[u][j + k], dp[u][j] + dp[v][k] + f(edge[i].w, k, v)); } son[u] += son[v]; } } int main () { scanf("%lld%lld", &n, &K); for (ll i = 1; i < n; i++) { ll u, v, w; scanf("%lld%lld%lld", &u, &v, &w); Insert(u, v, w); Insert(v, u, w); } dfs(1); printf("%lld\n", dp[1][K]); return 0; }