题面
一个树形dp,
我们根据题意去想如何得到两两距离,发现一条边的两侧每有一对同色点,这条边就要被经过一次
在当前的子节点的子树中,枚举有k个黑点,需要一个在其他子树中选了共 j - k 个黑点的状态
code
#include<bits/stdc++.h> #define int long long using namespace std; int n,m; int dp[2021][2021]; //i为根j个黑点 int head[4022],nxt[4022],to[4022],edge[4022]; int siz[2022]; int _; void add(int x,int y,int z) { _++; edge[_]=z; to[_]=y; nxt[_]=head[x]; head[x]=_; return ; } //看边经过次数 void makedp(int x,int fa) { siz[x]=1; dp[x][0]=0; dp[x][1]=0; //两种必然存在的结果 for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==fa) continue; makedp(y,x); siz[x]+=siz[y]; //减少范围,最多子树全是 黑点 for(int j=min(m,siz[x]);j>=0;j--) {//最多是儿子的子树全是 ,我们尝试去更新已知 for(int k=0;k<=min(j,siz[y]);k++) {//子树超了,状态不河狸 if(dp[x][j-k]==-1) continue; //edge[i]*k*(m-k) + edge[i]*(siz[y]-k)*(n-m-siz[y]+k) //边权*个数*个数(左右)(假设去染) +边权*子树个数*除这个子子树(未染) //当前点的j儿子=max(自身,儿子的dp值(有两个,黑/白)+) // 白, 黑 dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]+edge[i]*k*(m-k)+edge[i]*(siz[y]-k)*(n-m-siz[y]+k)); } } } return ; } signed main() { ios::sync_with_stdio(false); cin>>n>>m; memset(dp,-1,sizeof(dp)); for(int i=1;i<n;i++) { int q,w,e; cin>>q>>w>>e; add(q,w,e); add(w,q,e); } makedp(1,0); //根节点1,有m个黑点 cout<<dp[1][m]; return 0; }