BZOJ P4033 LOJ 2124 [HAOI2015] 树上染色【树形DP+背包】

题目分析(以下的 M M 都是题目当中输入的 K K ):

树形 DP D P (好吧这是废话)。我们按照树形 DP D P 的套路(啥?你不知道树形DP的套路?)很容易得出这道题的状态设定:

DP[I][J] D P [ I ] [ J ] 表示以 I I 为根的子树当中涂了 J J 个黑点的最大收益 J<=M J <= M

那么问题来了,我们应该如何进行状态转移,关键点在于应该如何处理状态转移时发生的收益变化?

让我们来考虑这样一个问题:

树上一条边 EdgeXY E d g e ( X , Y ) ,不妨设 X X Y Y 的父亲节点,我们假设不在以 X X 为根的子树外(包括 X X 点)一共有 P P 个黑点,那么在以 X X 为根的子树内也就是 Y Y 内(不包括 X X 点,包括 Y Y 点)一共有 MP M − P 个黑点。关键点来了,根据乘法原理所有的黑点连边那么一共有 P(MP) P ∗ ( M − P ) 条边,这 P(KP) P ∗ ( K − P ) 条边都经过同一条边 X>Y X − > Y ,白点同此。这样我们就将需要计算的距离转化为了某一条边的计算,这样就方便了我们的转移。

于是就可以得到下面的状态转移方程了(其实是一个树上背包的过程):

Size[X] S i z e [ X ] 表示以 X X 为根节点的子树的大小,

DP[X][J]=maxDP[X][J]DP[X][JK]+DP[Y][K]+K(JK)(X>Y)+(Size[Y]K)(NMSize[Y]+K)(X>Y) D P [ X ] [ J ] = m a x D P [ X ] [ J ] , D P [ X ] [ J − K ] + D P [ Y ] [ K ] + K ∗ ( J − K ) ∗ ( X − > Y ) + ( S i z e [ Y ] − K ) ∗ ( N − M − S i z e [ Y ] + K ) ∗ ( X − > Y )

由于在这棵树当中哪一个点是根节点都是相对的,为了方便讨论与写代码,都以 1 1 号点为根节点,所以最后的答案为 DP[1][M] D P [ 1 ] [ M ]

另,这道题似乎卡时,大家就不要在不必要的地方开 long long l o n g   l o n g

于是现在代码就很好写啦,关键代码:

void Tree_DP(int X,int Fx){
    int I,J,K;Size[X]=1;
    for(I=Head[X];I;I=Next[I]){
        int Y=To[I];
        if(Y!=Fx){
            Tree_DP(Y,X);
            Size[X]+=Size[Y];
        }
    }
    for(I=0;I<=Size[X];I++){
        DP[X][I]=-1ll;
    }DP[X][0]=DP[X][1]=0ll;
    for(I=Head[X];I;I=Next[I]){
        int Y=To[I];LL Z=Edge[I];
        if(Y!=Fx){
            int NumX=min(M,Size[X]);
            for(J=NumX;J>=0;J--){
                int NumY=min(J,Size[Y]);
                for(K=0;K<=min(NumY,J);K++){
                    if(DP[X][J-K]!=-1ll){
                        DP[X][J]=max(DP[X][J],DP[X][J-K]+DP[Y][K]+(LL)K*(M-K)*Z+(LL)(Size[Y]-K)*(N-M-Size[Y]+K)*Z);
                    }
                }
            }
        }
    }
}

你可能感兴趣的:(LOJ,动态规划-树形DP,动态规划-背包DP,BZOJ,动态规划与递推)