bzoj4033
树形DP
f[i][j]就表示以i为根的子树中染了多少个黑点。
我们可以发现,假设枚举了一条边i(x,y,c),那么它对收益的贡献就是c*(x那边黑点的个数*y那边黑点的个数+x那边白点的个数*y那边白点的个数)。
假设在做以x为根的子树,枚举到了它的儿子y,然后就枚举x子树中黑点的个数i和y子树中黑点的个数j(注意:此时y还没归到x中,即这是个独立的枚举,x中的黑点只包含已经跑过了的儿子的)。那么这条边的贡献就有c*[j*(K-j)+(size[y]-j)*( n-K-(size[y]-j) )]。就是以x->y这条边来分界。
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define maxn 2002 using namespace std; long long f[maxn][maxn],dp[maxn]; int w[maxn<<1],n,k,cnt,siz[maxn],head[maxn],nex[maxn<<1],to[maxn<<1]; void read(int &x){ char ch=getchar();x=0;int f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} x*=f; } void insert(int u,int v,long long ww){ to[++cnt]=v;nex[cnt]=head[u];w[cnt]=ww;head[u]=cnt; } void solve(int now,int fa){ siz[now]=1; for(int i=head[now];i;i=nex[i]){ if(to[i]==fa)continue; solve(to[i],now); memset(dp,0,sizeof dp); int szx=min(siz[now],k),szy=min(siz[to[i]],k); for(int ii=0;ii<=szx;ii++) for(int j=0;j<=szy;j++){ if(ii+j>k)continue; long long tmp=(long long)j*(k-j)+(siz[to[i]]-j)*(n-k-siz[to[i]]+j); dp[ii+j]=(long long)max(dp[ii+j],f[now][ii]+f[to[i]][j]+tmp*w[i]); } for(int ii=0;ii<=k;ii++)f[now][ii]=dp[ii]; siz[now]+=siz[to[i]]; } } int main(){ read(n);read(k); int x,y,z; for(int i=1;i<n;i++){ read(x);read(y);read(z); insert(x,y,z);insert(y,x,z); } solve(1,0); printf("%lld\n",f[1][k]); }