题目描述:
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
问收益最大值是多少。
题目分析:
由于无法确定每个黑点的具体深度,所以我们将贡献分在每条边上统计。
f
[
u
]
[
i
]
f[u][i]
f[u][i]表示
u
u
u子树内选
i
i
i个黑点时子树中每条边的贡献的和的最大值。
转移就看边
(
u
,
v
)
(u,v)
(u,v)两边的黑点对数以及白点对数即可。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 2005
#define LL long long
using namespace std;
int n,K,siz[maxn];
LL f[maxn][maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],tot;
inline void line(int x,int y,int z){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,w[tot]=z;}
void dfs(int u,int ff){
siz[u]=1;
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
dfs(v,u);
for(int j=max(K-(n-siz[v]),0);j<=siz[v]&&j<=K;j++) f[v][j]+=1ll*(j*(K-j)+(siz[v]-j)*(n-siz[v]-(K-j)))*w[i];
for(int j=siz[u];j>=0;j--)
for(int k=siz[v];k>=0;k--)
f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]);
siz[u]+=siz[v];
}
}
int main()
{
scanf("%d%d",&n,&K);
for(int i=1,x,y,z;i<n;i++) scanf("%d%d%d",&x,&y,&z),line(x,y,z),line(y,x,z);
dfs(1,0);
printf("%lld\n",f[1][K]);
}