树形DP
神题一点不会。。。
考虑每条边对答案的贡献。设 表示以 为根的子树中有 个黑点时对答案的贡献,则有 ,其中 为 的儿子, 表示 到 这条边对答案的贡献,即子树内黑/白点的数量 子树外黑/白点的数量 这条边的长度。
注意循环的正反和范围,具体见代码。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2005
using namespace std;
typedef long long LL;
struct edge{ int nxt,to; LL d; }ed[N<<1];
int n,m,k,h[N],fa[N],sz[N];
LL f[N][N];
#define addedge(x,y,z) ed[++k]=(edge){h[x],y,z},h[x]=k
void dfs(int x){
sz[x]=1,f[x][0]=f[x][1]=0;
for (int i=h[x],v;i;i=ed[i].nxt)
if ((v=ed[i].to)!=fa[x]){
fa[v]=x,dfs(v);
for (int j=min(sz[x]+sz[v],m);~j;j--)
//这里要倒序,或者另开一个数组存上一次的结果
for (int p=max(0,j-sz[x]);p<=min(sz[v],j);p++){
//这里开始范围要取max,因为黑点个数不可能超过子树大小
LL w=(1ll*p*(m-p)+1ll*(sz[v]-p)*(n-m-sz[v]+p))*ed[i].d;
f[x][j]=max(f[x][j],f[x][j-p]+f[v][p]+w);
}
sz[x]+=sz[v];
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1,x,y,z;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z),addedge(y,x,z);
}
for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
f[i][j]=-1e9;//一定要给负
return dfs(1),printf("%lld",f[1][m]),0;
}