51nod 苹果曼和树 (树形dp)

汉语题:

苹果曼有一棵n个点的树。有一些(至少一个)结点被标记为黑色,有一些结点被标
记为白色。
现在考虑一个包含k(0 ≤ k < n)条树边的集合。如果苹果曼删除这些边,那么会将这
个树分成(k+1)个部分。每个部分还是一棵树。
现在苹果曼想知道有多少种边的集合,可以使得删除之后每一个部分恰好包含一个黑
色结点。答案对1000000007 取余即可。

思路:

刚开始写树形dp,很难找出状态之间的关系,而这道题也是如此,桑心,
我们定义:dp[i][0~1]表示当前节点i是否有黑点的方案数,那么就有两个状态,分别
是dp[i][0],dp[i][1]表示分别i节点没黑点和有黑点,那么现在思考怎么转移状态,
1. dp[i][0]:和子树连接的话:只能其子树有没有黑节点都得连接
总的方案数是:dp[x][0]*(dp[son][0]+dp[son][1])
2. dp[i][1]:如果i节点有黑点和子树也有黑节点则只能不连接,方案数
dp[x][1]*(dp[son][0]+dp[son][1]),没黑点的话只能和dp[j][1]连接:
dp[x][0]*dp[son][1]

  • i连通块有黑色结点,j连通块有黑色结点,则i和j不能相连;
  • i连通块有黑色结点,j连通块无黑色结点,则i必须与j相连;
  • i连通块无黑色结点,j连通块有黑色结点,则i可以与j相连;
  • i连通块无黑色结点,j连通块无黑色结点,则i必须与j相连;
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;

const int MAXN = 100005;
const int mod = 1000000007;

int n;
vector<int >G[MAXN];
long long  dp[MAXN][2];
int v[MAXN];

void dfs(int x,int fa)
{
    if(v[x]) dp[x][1] = true;
    else dp[x][0] = true;
    int size = G[x].size();
    for(int i = 0;i < size; i++) {
        int son = G[x][i];
        if(fa != son) {
            dfs(son,x);
            dp[x][1] = (dp[x][1]*(dp[son][0]+dp[son][1]) + dp[x][0]*dp[son][1])%mod;
            dp[x][0] = (dp[x][0]*(dp[son][0]+dp[son][1]))%mod;
        }
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i = 1;i < n; i++) {
        int temp;
        scanf("%d",&temp);
        G[temp].push_back(i);
        G[i].push_back(temp);
    }
    for(int i = 0;i < n; i++)
        scanf("%d",&v[i]);
    dfs(0,-1);
    printf("%lld\n",dp[0][1]);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值