正题
题目链接:https://www.luogu.com.cn/problem/P3177
题目大意
nnn个点的一棵树,将kkk个点染成黑色,其他是白色,使得黑点之间和白点之间两两距离和最大。
解题思路
也是统计每条边的贡献,设fx,if_{x,i}fx,i表示节点xxx的子树中有iii个黑点时的最大子树贡献和。
发现这样转移是O(nk2)O(nk^2)O(nk2)的,只要限制iii的枚举范围在sizxsiz_xsizx以内就变成O(n2)O(n^2)O(n2)了,原因是节点之间两两做了一次贡献。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2100;
struct node{ll to,next,w;
}a[N*2];
ll n,K,tot,ls[N],f[N][N],g[N],siz[N];
void addl(ll x,ll y,ll w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return;
}
void dp(ll x,ll fa){siz[x]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;dp(y,x);for(ll j=0;j<=siz[x];j++)g[j]=f[x][j],f[x][j]=0;for(ll j=0;j<=siz[x];j++)for(ll k=0;k<=siz[y];k++)f[x][j+k]=max(f[x][j+k],g[j]+f[y][k]+a[i].w*(k*(K-k)+(siz[y]-k)*(n-K-siz[y]+k))); siz[x]+=siz[y];}return;
}
int main()
{scanf("%lld%lld",&n,&K);for(ll i=1;i<n;i++){ll x,y,w;scanf("%lld%lld%lld",&x,&y,&w);addl(x,y,w);addl(y,x,w);}dp(1,1);printf("%lld",f[1][K]);
}