bzoj4033: [HAOI2015]树上染色

这题很有思考价值。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\n",f[1][m]);
    return 0;
}

 

posted @ 2018-03-13 19:39  AKCqhzdy  阅读(116)  评论(0编辑  收藏  举报