题意:给出一棵树,每个节点的颜色为黑色或者白色,现在可以切割任意条边,使得答案这个树被分成若干块,每块至多一个黑色点。问方案数有多少。
对于树dp,一般用
dp[u]
表示当前u节点的子树的方案数,然后根据其与子节点的关系进行转移。
对于这题我们还需要记录,当前子树中是否已经有黑色点了,如果有,就可以切割,没有,就不能切割。这样状态的构造和转移都已经出来了。
dp[u][0/1]
,代表当前u节点的子树中,是否有黑色节点。
dp[u][1]=dp[u][1]∗dp[v][0](不断边)+dp[u][1]∗dp[v][1](断边)+dp[u][0]∗dp[v][1](不断边)
dp[u][0]=dp[u][0]∗dp[v][0](不断边)+dp[u][0]∗dp[v][1](断边)
需要注意 dp[u][1] 的转移中用到的是与当前的v讨论关系之前的 dp[u][0] ,因此转移的顺序需要注意下。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
const LL maxn = 110000;
namespace treedp {
LL n;
vector<LL> G[maxn];
LL dp[maxn][2];
void dfs(LL u, LL fa) {
for(LL i = 0; i < G[u].size(); i++) {
LL v = G[u][i];
if(v == fa) continue;
dfs(v, u);
dp[u][1] = (dp[u][1] * (dp[v][0] + dp[v][1]) % mod + dp[u][0] * dp[v][1]) % mod;
dp[u][0] = (dp[u][0] * (dp[v][0] + dp[v][1])) % mod;
}
}
void solve() {
scanf("%d", &n);
for(LL i = 1; i < n; i++) {
LL p;
scanf("%lld", &p);
G[p].push_back(i);
G[i].push_back(p);
}
for(LL i = 0; i < n; i++) {
LL v;
scanf("%lld", &v);
dp[i][v] = 1;
}
dfs(0, -1);
printf("%d\n", dp[0][1]);
}
}
int main() {
treedp::solve();
return 0;
}