题目链接:https://vjudge.net/contest/373128#problem/A
题意:给出一颗树有n个结点,每个结点有黑白颜色之分,给出n-1条边,问有多少种方案可以把树分成多个有且只有一个黑色结点的子树。
解题思路:
dp[i][1]表示以i为根的只有一个黑色结点的子树方案个数
dp[i][0]表示以i为根的没有黑色结点的子树方案个数
令to为与i相连的结点,则
dp[i][1]=dp[i][0]dp[to][1]+dp[i][1](dp[to][0]+dp[to][1]);
dp[i][1]它来自两部分,前半部分容易理解
后半部分中dp[i][1]*dp[to][0]容易理解,表示i子树有一个黑色结点,to子树没有黑色结点,将i和to合并的方案数
dp[i][1]*dp[to][1]表示i的子树有一个黑色结点,to的子树有一个黑色结点,将i点和to点之间分隔开的方案数
PS:注意dp数组要开long long
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define ll long long
const int mod=1e9+7;
const int maxn=1e5+10;
int n;
int color[maxn];
ll dp[maxn][2];
vector<int> link[maxn];
void dfs(int x)
{
dp[x][color[x]]=1;
int len=link[x].size();
for(int i=0;i<len;i++){
int to=link[x][i];
dfs(to);
dp[x][1]=(dp[x][1]*(dp[to][1]+dp[to][0])%mod+dp[x][0]*dp[to][1]%mod)%mod;
dp[x][0]=dp[x][0]*(dp[to][0]+dp[to][1])%mod;
}
}
int main()
{
while(scanf("%d",&n)!=EOF){
memset(dp,0,sizeof(dp));
memset(link,0,sizeof(link));
int to;
for(int i=1;i<n;i++){
scanf("%d",&to);
link[to].push_back(i);
}
for(int i=0;i<n;i++)
scanf("%d",&color[i]);
dfs(0);
printf("%lld\n",dp[0][1]);
}
return 0;
}