题目大意:有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。问收益最大值是多少
题解:f[i][j]表示以i为根的子树染了j个黑点对答案的最大贡献
考虑计算子树内的点与子树外的点组合所产生的权值
统计所有边对答案的贡献,一条边对答案贡献为
val∗(子树内黑∗子树外黑+子树内白∗子树外白)
因为有些状态不能到到达
实现的时候需要由合法状态顺着转移,会快很多
我的收获:Orz
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=2005;
#define INF 1e12
int n,m;
int t,head[M];
int size[M];
long long f[M][M];
struct edge{int to,val,nex;}e[M*2];
void add(int u,int v,int w){e[t]=(edge){v,w,head[u]};head[u]=t++;}
void dfs(int x,int fa)
{
size[x]=1;
for(int i=head[x];i!=-1;i=e[i].nex) if(e[i].to!=fa) dfs(e[i].to,x),size[x]+=size[e[i].to];
for(int i=2;i<=min(m,size[x]);i++) f[x][i]=-INF;
for(int i=head[x];i!=-1;i=e[i].nex)
{
int v=e[i].to;
if(v==fa) continue;
for(int j=min(size[x],m);j>=0;j--)
for(int k=0;k<=min(size[v],m)&&k<=j;k++){
long long costblack=(long long)e[i].val*k*(m-k);
long long costwhite=(long long)e[i].val*(size[v]-k)*(n-size[v]-(m-k));
f[x][j]=max(f[x][j],f[x][j-k]+f[v][k]+costblack+costwhite);
}
}
}
void work()
{
dfs(1,0);
cout<<f[1][m]<<endl;
}
void init()
{
int x,y,z;
cin>>n>>m;memset(head,-1,sizeof(head));
for(int i=1;i<n;i++) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
}
int main()
{
init();
work();
return 0;
}