【Codeforces 461B】Appleman and Tree

树形DP

题目大意:

给定一棵树,每个结点为黑色或白色。

现在可以切掉若干条边,要求满足切掉后的连通块中,每个连通块有且仅有一个黑色结点。

求方案数。

题解:

树形dp。

令 $f_{i,0/1}$ 表示子树 $i$ 中,与 $i$ 连通的那个连通块中有黑色结点/无黑色结点,其他部分已经合法的方案数。

没有黑色结点的连通块不能自己封闭,而有黑色结点的连通块不能再和黑色结点合并。

第一行的意思是,当前结点不能有黑色结点的状态,对于当前结点的每棵子树,要么和没黑色结点的连通块合并,要么和有黑色结点的连通块切断。

第二行的意思是,当前结点有黑色结点的状态,对于当前结点的每棵子树,要么和没黑色结点的连通块合并,要么和有黑色结点的连通块切断。或者,从没有黑色结点的状态,通过和一个有黑色结点的连通块合并后得到。

时间复杂度 $O(n)$。

Code:

#include<iostream>
#include<cstdio>
using namespace std;
const int md=1e9+7,N=1e5+6;
typedef long long LL;
int n,f[N][2],fa[N],head[N],cnt,col[N];
struct edge{
    int to,nxt;
}e[N];
void dfs(int now){
    f[now][col[now]]=1;
    for(int i=head[now];i;i=e[i].nxt){
        dfs(e[i].to);
        f[now][1]=((LL)f[now][1]*(f[e[i].to][0]+f[e[i].to][1])+(LL)f[now][0]*f[e[i].to][1])%md;
        f[now][0]=(LL)f[now][0]*(f[e[i].to][0]+f[e[i].to][1])%md;
    }
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=2;i<=n;++i){
        cin>>fa[i],++fa[i];
        e[++cnt]=(edge){i,head[fa[i]]},head[fa[i]]=cnt;
    }
    for(int i=1;i<=n;++i)
        cin>>col[i];
    dfs(1);
    cout<<f[1][1]<<'\n';
    return 0;
}