bzoj4033[HAOI2015] 树上染色

题目链接:bzoj4033
题目大意:
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。问收益最大值是多少。

题解:
treedp
可能很好想。。?但是我自己想的转移很迷啊(为了不暴露智商还是不要说了
说正解吧
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这条边来分界嘛。
然后就没什么好讲了吧。。

神tm漏了一个等号调了我一年。。
zz的我忘了家里存的是男神号的帐密直接交了。A了就算了我还WA了一发qwq。虽然很快改A了(抱着不让这个号留下以WA结尾的心理,A掉之后又上那个号交了一遍= =)。但是当我看到这个号之前可能是不同的人交了三次的时候,我觉得好愧疚(?)..希望他没有发现orz
对了,让我WA的罪魁祸首就是longlong,题目对于边权的大小什么也没说!我之前还特地看了一遍来着(如果是我眼瞎当我没说qwq)!于是我就直接一秒替换了。

#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
#define maxn 2010

struct node
{
    LL x,y,c,next;
}a[maxn*2];LL len,first[maxn];
LL n,K,size[maxn],f[maxn][maxn],ls[maxn];
LL mymin(LL x,LL y){return (xreturn (x>y)?x:y;}
void ins(LL x,LL y,LL c)
{
    len++;a[len].x=x;a[len].y=y;
    a[len].c=c;a[len].next=first[x];first[x]=len;
}
void treedp(LL x,LL fa)
{
    LL i,j,k;size[x]=1;
    for (k=first[x];k!=-1;k=a[k].next)
    {
        LL y=a[k].y;
        if (y==fa) continue;
        treedp(y,x);
        for (i=0;i<=K;i++) ls[i]=0;
        LL sizx=mymin(K,size[x]);
        LL sizy=mymin(K,size[y]);
        for (i=0;i<=sizx;i++)
         for (j=0;j<=sizy;j++) if (i+j<=K)
         {
             LL tt=j*(K-j)+(size[y]-j)*(n-K-(size[y]-j));
             ls[i+j]=mymax(ls[i+j],f[x][i]+f[y][j]+tt*a[k].c);
         }
        for (i=0;i<=K;i++) f[x][i]=ls[i];
        size[x]+=size[y];
    }
}
int main()
{
    LL i,x,y,c;
    scanf("%lld%lld",&n,&K);
    len=0;memset(first,-1,sizeof(first));
    for (i=1;iscanf("%lld%lld%lld",&x,&y,&c);
        ins(x,y,c);ins(y,x,c);
    }
    memset(f,0,sizeof(f));
    treedp(1,0);
    printf("%lld\n",f[1][K]);
    return 0;
}

你可能感兴趣的:(树形dp)