题意:一棵树节点被染成白色和黑色,问有多少种切割方法使得切分成的每一个部分恰好包含一个黑色节点
思路:树形DP。dp[u][0]表示包含这个节点的子树切分完以后没有一个黑色节点的方法数,dp[u][1]表示包含这个节点的子树切割完以后恰好有一个黑色节点的方法数。如果这个节点是黑色的,那么dp[u][0]只能为0。当它的儿子有一个黑色的时候,可以选择把它跟它的儿子切开,如果它的儿子没有黑色的时候,就不切开。如果节点u是白色,那么要使这个节点所在的子树有一个黑色,这个黑色必然是从儿子里来的;要使这个节点所在节点没有黑色,如果它的儿子没有黑色,就不切开,如果有黑色,就切开。
状态转移方程:
黑色节点:
dp[u][1]=dp[u][1]*(dp[vv][1]+dp[vv][0])
白色节点:
dp[u][1]=dp[u][1]*(dp[vv][1]+dp[vv][0])+dp[u][0]*dp[vv][1];
dp[u][0]=dp[u][0]*(dp[vv][0]+dp[vv][1]);
代码如下:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=200005;
const int mod=1000000007;
typedef long long ll;
struct Edge
{
int u,v,next;
}edge[maxn];
int tot;
int first[maxn];
int color[maxn];
void add(int uu,int vv)
{
edge[tot].u=uu;
edge[tot].v=vv;
edge[tot].next=first[uu];
first[uu]=tot++;
}
ll dp[maxn][2];
void dfs(int u,int fa)
{
if(color[u])
{
dp[u][0]=0;
dp[u][1]=1;
}
else
{
dp[u][1]=0;
dp[u][0]=1;
}
for(int i=first[u];i!=-1;i=edge[i].next)
{
int vv=edge[i].v;
if(vv==fa)continue;
dfs(vv,u);
if(color[u])
{
dp[u][1]=dp[u][1]*(dp[vv][1]+dp[vv][0])%mod;
}
else
{
dp[u][1]=dp[u][1]*(dp[vv][1]+dp[vv][0])%mod+dp[u][0]*dp[vv][1]%mod;
dp[u][1]%=mod;
dp[u][0]=dp[u][0]*(dp[vv][0]+dp[vv][1])%mod;
}
}
}
int main()
{
// freopen("data.txt","r",stdin);
tot=0;
memset(first,-1,sizeof(first));
int n;
scanf("%d",&n);
for(int i=0;i<n-1;++i)
{
int u;
scanf("%d",&u);
add(i+1,u);
add(u,i+1);
}
for(int i=0;i<n;++i)
{
scanf("%d",&color[i]);
}
dfs(0,-1);
printf("%lld\n",dp[0][1]);
return 0;
}