这道题最关键的思想在于,我们以往树形DP的时候,常常设f[i][j]表示点i的子树的dp值,而这道题对于一个点的子树,它的贡献是子树内所有黑点到子树的根节点的距离之和乘上其兄弟的子树的黑点个数,白点同理,因为只能染k个,所以必须记录染了几个黑点,这样的状态是O(n3)的
所以我们需要换一种统计贡献的方法,既然点不好统计,何不统计边呢?
一条边的贡献就是其两端点的子树内的黑点个数之积加上白点个数之积,再乘上边权
那么设f[i][j]表示点i的子树内染j个黑点的最大收益,转移就统计边的贡献就好了
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=2005;
int n,K;
int siz[N],pt[N],vis[N<<1],head[N<<1],nxt[N<<1],tot=0;
ll dep[N],f[N][N],c[N<<1];
inline void add(int x,int y,ll z){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;}
void dfs(int v){
pt[v]=siz[v]=1;f[v][0]=f[v][1]=0;
for(int i=head[v];i;i=nxt[i]){
int y=vis[i];
if(pt[y]) continue;
dep[y]=c[i];dfs(y);
for(int j=min(siz[v],K);~j;j--)
for(int k=min(siz[y],K-j);~k;k--)
f[v][j+k]=max(f[v][j+k],f[v][j]+f[y][k]);
siz[v]+=siz[y];
}
for(int i=0;i<=min(siz[v],K);i++) f[v][i]+=dep[v]*(i*(K-i)+(siz[v]-i)*(n-K-siz[v]+i));
}
int main(){
n=read(),K=read();
for(int x,y,z,i=1;i<n;i++) x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z);
memset(f,128,sizeof(f));
dfs(1);
cout<<f[1][K];
return 0;
}