淘先锋技术网

首页 1 2 3 4 5 6 7

题目链接: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;
}