这题很有思考价值。DP啊,真是太TM博大精深了。%%%题解a
相信只要用treeDP就会想到二维,这东西表示答案也很容易想,难就难在怎么转移。
对于当前的dfs,我们想要知道,有多少个点对会经过x和fa的这条边,而且是全局总贡献(这想法真是太NB了)维护的话就可以写一个背包,每访问一个节点就维护一下最值。
主要问题在于这一句:
for(LL i=0;i<=min(tot[x],m);i++) f[x][i]+=d*(i*(m-i)+(tot[x]-i)*((n-tot[x])-(m-i)));
啥意思?对于当前,i是子树中黑色的数,乘上剩下的黑色数,因为既然不在当前子树,那肯定得过D,同理,后面这个就是白色数。这样就把全局的答案求出来了。
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; struct node { int x,y,next;LL d; }a[4100];int len,last[2100]; void ins(int x,int y,LL d) { len++; a[len].x=x;a[len].y=y;a[len].d=d; a[len].next=last[x];last[x]=len; } LL n,m,tot[2100],f[2100][2100]; void dfs(int x,int fa,LL d) { f[x][0]=f[x][1]=0;tot[x]=1; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa) { dfs(y,x,a[k].d); for(int i=min(tot[x],m);i>=0;i--) for(int j=min(tot[y],m-i);j>=0;j--) f[x][i+j]=max(f[x][i+j],f[x][i]+f[y][j]); tot[x]+=tot[y]; } } for(LL i=0;i<=min(tot[x],m);i++) f[x][i]+=d*(i*(m-i)+(tot[x]-i)*((n-tot[x])-(m-i))); } int main() { int x,y;LL d; scanf("%lld%lld",&n,&m); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { scanf("%d%d%lld",&x,&y,&d); ins(x,y,d);ins(y,x,d); } dfs(1,0,0); printf("%lld ",f[1][m]); return 0; }