汉语题:
苹果曼有一棵n个点的树。有一些(至少一个)结点被标记为黑色,有一些结点被标
记为白色。
现在考虑一个包含k(0 ≤ k < n)条树边的集合。如果苹果曼删除这些边,那么会将这
个树分成(k+1)个部分。每个部分还是一棵树。
现在苹果曼想知道有多少种边的集合,可以使得删除之后每一个部分恰好包含一个黑
色结点。答案对1000000007 取余即可。
思路:
刚开始写树形dp,很难找出状态之间的关系,而这道题也是如此,桑心,
我们定义:dp[i][0~1]表示当前节点i是否有黑点的方案数,那么就有两个状态,分别
是dp[i][0],dp[i][1]表示分别i节点没黑点和有黑点,那么现在思考怎么转移状态,
1. dp[i][0]:和子树连接的话:只能其子树有没有黑节点都得连接
总的方案数是:dp[x][0]*(dp[son][0]+dp[son][1])
2. dp[i][1]:如果i节点有黑点和子树也有黑节点则只能不连接,方案数
dp[x][1]*(dp[son][0]+dp[son][1]),没黑点的话只能和dp[j][1]连接:
dp[x][0]*dp[son][1]
- i连通块有黑色结点,j连通块有黑色结点,则i和j不能相连;
- i连通块有黑色结点,j连通块无黑色结点,则i必须与j相连;
- i连通块无黑色结点,j连通块有黑色结点,则i可以与j相连;
- i连通块无黑色结点,j连通块无黑色结点,则i必须与j相连;
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN = 100005;
const int mod = 1000000007;
int n;
vector<int >G[MAXN];
long long dp[MAXN][2];
int v[MAXN];
void dfs(int x,int fa)
{
if(v[x]) dp[x][1] = true;
else dp[x][0] = true;
int size = G[x].size();
for(int i = 0;i < size; i++) {
int son = G[x][i];
if(fa != son) {
dfs(son,x);
dp[x][1] = (dp[x][1]*(dp[son][0]+dp[son][1]) + dp[x][0]*dp[son][1])%mod;
dp[x][0] = (dp[x][0]*(dp[son][0]+dp[son][1]))%mod;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d",&n);
for(int i = 1;i < n; i++) {
int temp;
scanf("%d",&temp);
G[temp].push_back(i);
G[i].push_back(temp);
}
for(int i = 0;i < n; i++)
scanf("%d",&v[i]);
dfs(0,-1);
printf("%lld\n",dp[0][1]);
return 0;
}