Solution:
- 定义状态(dp[i][j])表示(i)号节点为根节点的子树里面有(j)个黑色节点时最大的贡献值
- 然后我们要知道的就是子节点到根节点这条边会计算次数就是:子树中白色节点数(*)子树外白色节点数(+)子树中黑色节点数(*)子树外黑色节点数
[dp[u][j+k]=max(dp[u][j+k],
]
[dp[u][j]+dp[v][k]+(1ll)*k*(m-k)*dis[v]+(1ll)*(siz[v]-k)*(n-m-siz[v]+k)*dis[v])
]
Attention:
- 树上背包dp注意操作:
- 这样可以保证时间复杂度是(O(n^2)),每次会保证是从已经获得的dp值推向未知的,就不会有多余的操作,所以我们每次枚举要添加的节点数目,加到已经求出前面几棵子树节点数目中
for(int j=min(m,siz[u]);j>=0;j--){
int box=min(m,siz[v]);
for(int k=box;k>=0;k--){
dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]+1ll*k*(m-k)*dis[v]+1ll*(siz[v]-k)*(n-m-siz[v]+k)*dis[v]);
}
}siz[u]+=siz[v];
Code:
//It is coded by Ning_Mew on 4.24
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=2000+7;
int n,m,fa[maxn];
LL dp[maxn][maxn];
int siz[maxn],dis[maxn];
int head[maxn],cnt=0;
struct Edge{int nxt,to,dis;}edge[maxn*2];
void add(int from,int to,int dis){
edge[++cnt].nxt=head[from];
edge[cnt].to=to;
edge[cnt].dis=dis;
head[from]=cnt;
}
void dfs(int u){
siz[u]=1;//dp[u][0]=dp[u][1]=0;
for(int i=head[u];i!=0;i=edge[i].nxt){
int v=edge[i].to; if(v==fa[u])continue;
dis[v]=edge[i].dis; fa[v]=u;
dfs(v); //siz[u]+=siz[v];
for(int j=min(m,siz[u]);j>=0;j--){
int box=min(m,siz[v]);
for(int k=box;k>=0;k--){
dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]+1ll*k*(m-k)*dis[v]
+1ll*(siz[v]-k)*(n-m-siz[v]+k)*dis[v]);
}
}siz[u]+=siz[v];
}return;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n-1;i++){
int u,v,diss;scanf("%d%d%d",&u,&v,&diss);
add(u,v,diss);add(v,u,diss);
}
dfs(1);
printf("%lld
",dp[1][m]);
return 0;
}