Codefest 17 C. Helga Hufflepuff's Cup(树形DP)

题意:

在一棵树上进行涂色,总共有 m 种颜色(编号 1m ),其中编号为 k 的颜色为特殊颜色,如果树上一个节点染上了特殊颜色,则其邻居节点的颜色编号都应该 <k ,求在这棵树上染色总共有多少种方法,染上特殊颜色的节点的数目不超过 x 个。
n105, m109, x10

思路:

难点:状态定义 + 状态转移方程细节
比较明显的树形dp。
首先是状态定义,如果我们仅定义:
dp[u][i][0] u 为根节点的子树,使用了 i 个特殊颜色,且该当前根节点没有使用特殊颜色;
dp[u][i][1] u 为根节点的子树,使用了 i 个特殊颜色,且该当前根节点使用了特殊颜色;
你会发现转移的时候, dp[u][i][0] 是没有办法求出来的。。。
所以我们定义:
dp[u][i][0] u 为根节点的子树,使用了 i 个特殊颜色,且该当前根节点颜色编号小于 k
dp[u][i][1] u 为根节点的子树,使用了 i 个特殊颜色,且该当前根节点颜色编号等于 k
dp[u][i][2] u 为根节点的子树,使用了 i 个特殊颜色,且该当前根节点颜色编号大于 k
所以转移的过程我们只需要根据限制条件来就可以了。。。
以求 dp[u][i][0] 为例,枚举子节点的特殊颜色贡献值 j(ji) ,此时子节点颜色编号可以取任意值,即总的方案数为: dp[u][ij][0](dp[v][j][0]+dp[v][j][1]+dp[v][j][2]) ,将这个值累加即可。

要注意的是,在 u 节点枚举 i 的时候,是从大往小枚举,因为这样可以确保我使用的 dp[u][ij][0] 是来自前一个 u 的子节点计算后得到的值,如果从小往大枚举,那么这个值就会被覆盖掉。

代码:

#include <bits/stdc++.h>
const int N = 100005, mo = 1000000007, X = 12;
int h[N],f1[N][X],f2[N][X],f3[N][X],n,m,k,z,u,v,xb,ans,i;
struct edge
{
    int to, next;
}e[N<<1];
inline void addedge(int u,int v)
{
    e[++xb] = (edge){v,h[u]}, h[u] = xb;
    e[++xb] = (edge){u,h[v]}, h[v] = xb;
}
void dfs(int x,int fa)
{
    f1[x][1] = 1,f2[x][0] = z-1,f3[x][0] = m-z;
    for(int i = h[x];i;i = e[i].next)
        if(e[i].to!=fa)
        {
            dfs(e[i].to,x);
            for(int j = k;j >= 0;-- j)
            {
                int s1 = 0, s2 = 0,s3 = 0;
                for(int l = 0;l <= j;++ l)
                {
                    if(l<j) s1 = (s1 + 1ll*f1[x][j-l] * f2[e[i].to][l])%mo;
                    s2 = (s2 + 1ll*f2[x][j-l] * (1ll*f1[e[i].to][l] + f2[e[i].to][l] + f3[e[i].to][l]))%mo;
                    s3 = (s3 + 1ll*f3[x][j-l] * (1ll*f2[e[i].to][l] + f3[e[i].to][l]))%mo;
                }
                f1[x][j] = s1,f2[x][j] = s2,f3[x][j] = s3;
            }
        }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(i = 1;i < n;++ i)
    {
        scanf("%d %d", &u, &v);
        addedge(u, v);
    }
    scanf("%d %d", &z, &k);
    dfs(1, 0);
    for(i = 0;i <= k;++ i) ans = (1ll*ans + f1[1][i] + f2[1][i] + f3[1][i]) % mo;
    return printf("%d\n", ans), 0;
}

猜你喜欢

转载自blog.csdn.net/u014686462/article/details/78090495
今日推荐