Codeforces 461B:Appleman and Tree
题目链接:Codeforces http://codeforces.com/contest/461/problem/B
51nod http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1500
题目大意:给定一颗有$n$个结点的树,各个结点的颜色为黑色或者白色,问有多少种边的集合,可以使得删除之后每一个部分恰好包含一个黑色结点。答案对$1000000007$ 取余。
树形DP
定义状态:$dp[u][1]$为$u$所在连通块中包含一个黑色结点的方案数,$dp[u][0]$为$u$所在连通块中不包含黑色结点的方案数。
任选一点为根结点(这里选取的是$0$结点),从子叶向上DP.
则初始状态为:
- 若$u$为黑色结点,则$dp[u][1]=1$,$dp[u][0]=0$
- 若$u$为黑色结点,则$dp[u][1]=0$,$dp[u][0]=1$
不妨设$v$为$u$的孩子结点,考虑这四种情况:
- $u$连通块有黑色结点,$v$连通块有黑色结点,则$v$和$u$不能相连;
- $u$连通块有黑色结点,$v$连通块无黑色结点,则$v$必须与$u$相连;
- $u$连通块无黑色结点,$v$连通块有黑色结点,则$v$可以与$u$相连;
- $u$连通块无黑色结点,$v$连通块无黑色结点,则$v$必须与$u$相连;
故转移方程为(注意式子顺序不能交换):
- $dp[u][1]=dp[u][1] \times (dp[v][1]+dp[v][0]) + dp[u][0] \times dp[v][1]$
- $dp[u][0]=dp[u][0] \times (dp[v][1]+dp[v][0])$
代码如下:
1 #include <cstdio> 2 #include <vector> 3 #define N 100005 4 using namespace std; 5 typedef long long ll; 6 const ll m=1000000007; 7 vector<int>e[N]; 8 int c[N],n,x; 9 ll dp[N][2]; 10 ll mul(ll a,ll b){return (a*b)%m;} 11 ll add(ll a,ll b){return (a+b)%m;} 12 void dfs(int u,int f){ 13 dp[u][c[u]]=1; 14 for(int i=0;i<(int)e[u].size();++i){ 15 int v=e[u][i]; 16 if(v!=f){ 17 dfs(v,u); 18 dp[u][1]=add(mul(dp[u][1],add(dp[v][1],dp[v][0])), mul(dp[u][0],dp[v][1])); 19 dp[u][0]=mul(dp[u][0],add(dp[v][1],dp[v][0])); 20 } 21 } 22 } 23 int main(void){ 24 scanf("%d",&n); 25 for(int i=1;i<n;++i){ 26 scanf("%d",&x); 27 e[x].push_back(i); 28 } 29 for(int i=0;i<n;++i) 30 scanf("%d",&c[i]); 31 dfs(0,-1); 32 printf("%d\n",dp[0][1]); 33 }