P3177 [HAOI2015]树上染色
“将我大汉军旗插遍大漠草原!”
封面图小姐姐好色气啊
及树上DP的一些总结qwq
首先说说这个题,dp[i][j]表示点i的子树里选了j个黑点目前得到的最大贡献是什么
贡献是指i子树里所有边的贡献,转移时考虑i到v这条边的贡献,两侧共有多少个黑白点乘一下就好啦
然后这样子好像再套个树形背包就解决了!
下面说下树形DP的坑点
-
转移时,我们枚举要从大到小枚举,第一重循环不这样做会导致完全背包,第二重循环不这样做如果循环内有0(比如这题0个黑点情况)就会导致先更新后又更新
-
要之后加,否则一条链就卡掉了
别的好像没啥了
再说下树的直径?
dp[u]表示点u最长链
当发现dp[v]+1>dp[u]时,我们先把这两条链拼起来更新答案再update u
别的没了qwq可能还有什么树上容斥?没做啊
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int MAXN = 3500;
int n, M, ccnt, N;
int dp[MAXN][MAXN], siz[MAXN];
int home[MAXN * 2], nxt[MAXN * 2], to[MAXN * 2], len[MAXN * 2];
inline void ct(int x, int y, int z) {
ccnt++;
nxt[ccnt] = home[x];
to[ccnt] = y;
home[x] = ccnt;
len[ccnt] = z;
}
inline void dfs(int u, int F) {
siz[u] = 1;
for(int i = home[u], val; i; i = nxt[i]) {
int v = to[i];
if(v == F)continue;
dfs(v, u);
for(int j = min(M, siz[u]); j >= 0; --j) {
for(int k = min(M, siz[v]); k >= 0; --k) {
val = len[i] * k * (M - k) + len[i] * (siz[v] - k) * (N - siz[v] + k);
dp[u][j + k] = max(dp[u][j + k], dp[u][j] + dp[v][k] + val);
}
}
siz[u] += siz[v];
}
return ;
}
signed main() {
scanf("%lld%lld", &n, &M);
N = n - M;
for(int i = 2, x, y, z; i <= n; ++i) {
scanf("%lld%lld%lld", &x, &y, &z);
ct(x, y, z);
ct(y, x, z);
}
dfs(1, 0);
printf("%lld\n", dp[1][M]);
return 0;
}