一棵带边权的树,你需要把 $k$ 个点染成黑色,剩下的染成白色,你会获得黑点间两两间距离之和 + 白点间两两距离之和的收益
求收益最大值
$n leq 5000$
sol:
树形 dp
显然一条边的贡献是 $边权 imes (左边白色 imes 右边白色 + 左边黑色 imes 右边黑色)$
$f_{(x,i)}$ 表示 $x$ 子树内选了 $i$ 个黑点,子树里的边对全局答案的贡献
然后我们按树上背包转移,转移的时候加的贡献其实是一条父子边的贡献
我们假设在 $v$ 点下面放了 $w$ 个黑点
这条边的贡献就是 $$边权 imes (w imes (k - w) + (size_v - k) imes (n - size_v - k + w))$$
还是很简单的
#include<bits/stdc++.h> #define LL long long #define int long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int inf = 1e16; int n,m,nk; struct EDG{int to,val;}; vector<EDG> G[2100]; int f[2100][2100],size[2100],tmp[2100]; inline void dfs(int x,int fa) { size[x] = 1; for(int i=0;i<G[x].size();i++) { int to = G[x][i].to,len = G[x][i].val; if(to == fa)continue; dfs(to,x); fill(tmp,tmp+size[x]+size[to]+1,-inf); for (int j=0;j<=size[x];j++) for (int k=0;k<=size[to];k++) tmp[j+k]=max(tmp[j+k],f[x][j]+f[to][k]+(LL)len*((LL)k*(m-k)+(LL)(n-size[to]-m+k)*(size[to]-k))); size[x]+=size[to]; for (int j=0;j<=size[x];j++) f[x][j]=tmp[j]; } } signed main() { n = read();m = read(); nk = n - m; for(int i=2;i<=n;i++) { int u = read(),v = read(),w = read(); G[u].push_back((EDG){v,w}); G[v].push_back((EDG){u,w}); } dfs(1,1); cout<<f[1][m]; }