这道题很妙啊。反正我是想不出来。。
首先有一个很容易想到的状态,设f[i][j]表示子树为i,i涂了j个节点的距离和。嗯这个还是能想到的,然后考虑转移,然后就被虐成zz了。。
分析子树i与儿子所连边的贡献,可以发现这条边的贡献等于他左右两边同色点数乘积和(左黑点数*右黑点数+左白点数*右白点数)。于是我们发现可以进行转移了,就是一个树形dp,f[x][j]=max(f[x][j],f[x][j-l]+f[t[i].v][l]+t[i].w*(l*(k-l)+(size[t[i].v]-l)*(n-size[t[i].v]-k+l)),解释一下,x为当前子树,枚举j(子树的节点数),枚举l(儿子以下的节点数)。
注意:long long炒鸡坑和初始化!!!
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#define ll long long
using namespace std;
const int Maxn=2010;
const ll oo=2e18;
struct node
{
int v;
int w;
int next;
}t[Maxn<<1];
ll f[Maxn][Maxn];
int head[Maxn],size[Maxn];
int n,k,cnt=0;
inline void Insert(int u,int v,int w)
{
cnt++;
t[cnt].v=v;
t[cnt].w=w;
t[cnt].next=head[u];
head[u]=cnt;
}
inline void dfs(int x,int fa)
{
size[x]=1;
f[x][0]=f[x][1]=0;
for(int i=head[x];i;i=t[i].next)
{
if(t[i].v==fa)
continue;
dfs(t[i].v,x);
size[x]+=size[t[i].v];
}
for(int i=head[x];i;i=t[i].next)
{
if(t[i].v==fa)
continue;
for(int j=min(size[x],k);j>=0;j--)
for(int l=0;l<=min(size[t[i].v],j);l++)
{
ll p=(((ll)t[i].w)*((ll)l)*((ll)(k-l)))+((ll)t[i].w)*((ll)(size[t[i].v]-l))*((ll)(n-size[t[i].v]-k+l));
f[x][j]=max(f[x][j],f[x][j-l]+f[t[i].v][l]+p);
}
}
}
int main()
{
int a,b,c;
scanf("%d%d",&n,&k);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&a,&b,&c);
Insert(a,b,c);
Insert(b,a,c);
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
f[i][j]=-oo;
dfs(1,0);
printf("%lld",f[1][k]);
return 0;
}