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]);
}

 

posted @ 2018-05-18 14:50  lnyzo  阅读(122)  评论(0编辑  收藏  举报