点之间距离不好直接求。
可以转化为边的贡献。(感觉是经典思维套路了)
然后就可以转化为经典树形dp题。(舞会那道)
dp[i][j],以i为根的子树,染色j个黑色点,所有边的贡献。
一个边u-v的贡献是:u这边黑点个数*v这边黑点个数*w+u这边白点个数*v这边白点个数*w
然后树上分组背包即可。
这题理论最坏情况下是ON^3的复杂度。
但我们加上if(dp[x][j-k]!=-1)这句话后,
有效执行次数就降到了 On^2 *T T 是一个小常数。
(虽然循环执行了n^3,但很多是continue,运行很快)
所以网上题解说复杂度On^2我是不认同的。
给你构造一个链,循环次数就是趋向于On^3(不信的话自己造数据跑一跑,只不过有效次数是在On^2,所以这题才能过)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI= acos(-1.0);
const int M = 2000+7;
int head[M],cnt=1;
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
int n,m;
ll dp[M][M];//以i为根的子树,染色j个黑色,其子树包含的边的贡献和
int siz[M];
//每条边的贡献为: (边两边的白点个数相乘+边两边的黑点个数相乘)*边权w
void dfs(int x,int fa)
{
siz[x]=1;
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to,w=ee[i].w;
if(y==fa)continue;
dfs(y,x);
siz[x]+=siz[y];
}
dp[x][0]=dp[x][1]=0;//x点染成黑色或白色,其余均为不合法。
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to,w=ee[i].w;
if(y!=fa)
for(int j=min(siz[x],m);j>=0;j--)
{
for(int k=0;k<=min(siz[y],j);k++)
{
//保证棵子树选一个染色方案。
if(dp[x][j-k]!=-1)
{
ll val=(ll)k*(m-k)*w+(ll)(siz[y]-k)*(n-m-siz[y]+k)*w;
dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]+val);
}
// k , siz[y]-k m-k , n-m-siz[y]+k
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
memset(dp,-1,sizeof(dp));
dfs(1,0);
//for(int i=0;i<=m;i++)
cout<<dp[1][m]<<endl;
return 0;
}