题意:
给出一棵n个节点的无根树,每个节点要么是0,要么是1,现在要把树分成若干区域,要求每个节点都必须属于一个区域,而且一个区域中只能有一个节点为1。问一共有多少种分法?
思路:
设dp[i][0]表示节点i属于某个没有1的区域的方案数,dp[i][1]表示节点i属于某个有1的区域的方案数。可以得到状态转移方程:
dp[u][0] = dp[u][0] * (dp[v][0] + dp[v][1]);
dp[u][1] = dp[u][1] * (dp[v][0] + dp[v][1]) + dp[u][0] * dp[v][1];
自下而上dfs进行一遍dp即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;
const ll MOD = 1e9 + 7;
vector <int> tree[MAXN];
ll dp[MAXN][2];
int a[MAXN];
void dfs(int u, int pre) {
dp[u][1] = a[u];
dp[u][0] = a[u] ^ 1;
int cnt = tree[u].size();
for (int i = 0; i < cnt; i++) {
int v = tree[u][i];
if (v == pre) continue;
dfs(v, u);
ll old[2] = {dp[u][0], dp[u][1]};
dp[u][0] = (old[0] * dp[v][0] % MOD + old[0] * dp[v][1] % MOD) % MOD;
dp[u][1] = (old[1] * dp[v][0] % MOD + old[1] * dp[v][1] % MOD + old[0] * dp[v][1] % MOD) % MOD;
}
}
int main() {
//freopen("in.txt", "r", stdin);
int n, x;
scanf("%d", &n);
for (int i = 1; i < n; i++) {
scanf("%d", &x);
tree[i].push_back(x);
tree[x].push_back(i);
}
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
dfs(0, -1);
printf("%I64d\n", dp[0][1]);
}