问题 A: Evensgn 剪树枝
时间限制: 1 Sec 内存限制: 128 MB题目描述
繁华中学有一棵苹果树。苹果树有 n 个节点(也就是苹果),n − 1 条边(也就
是树枝)。调皮的 Evensgn 爬到苹果树上。他发现这棵苹果树上的苹果有两种:一
种是黑苹果,一种是红苹果。Evensgn 想要剪掉 k 条树枝,将整棵树分成 k + 1 个
部分。他想要保证每个部分里面有且仅有一个黑苹果。请问他一共有多少种剪树枝
的方案?
输入
第一行一个数字 n,表示苹果树的节点(苹果)个数。
第二行一共 n − 1 个数字 p0, p1, p2, p3, ..., pn−2,pi 表示第 i + 1 个节点和 pi 节
点之间有一条边。注意,点的编号是 0 到 n − 1。
第三行一共 n 个数字 x0, x1, x2, x3, ..., xn−1。如果 xi 是 1,表示 i 号节点是黑
苹果;如果 xi 是 0,表示 i 号节点是红苹果。
输出
输出一个数字,表示总方案数。答案对 109 + 7 取模。
样例输入
样例输入 2 6 0 1 1 0 4 1 1 0 0 1 0 样例输入 3 10 0 1 2 1 4 4 4 0 8 0 0 0 1 0 1 1 0 0 1
样例输出
样例输出 1 2 样例输出 2 1 样例输出 3 27
提示
数据范围
对于 30% 的数据,1 ≤ n ≤ 10。
对于 60% 的数据,1 ≤ n ≤ 100。
对于 80% 的数据,1 ≤ n ≤ 1000。
对于 100% 的数据,1 ≤ n ≤ 105。
对于所有数据点,都有 0 ≤ pi ≤ n − 1,xi = 0 或 xi = 1。
特别地,60% 中、80% 中、100% 中各有一个点,树的形态是一条链。
之前这个人刚因为欠债把我坑了一回。。现在又来捡树枝了呜呜呜~~~~(>_<)~~~~
一眼望去,树归无际。。没错!这道题就是一个很狠狠狠的树归!!
::
我们设f[i][0]为以i为根节点是如果有k个黑苹果,那就正好对其子树剪了k刀,f[i][1]为对其子树剪了k-1刀
因为我们发现如果总共有p个黑苹果,对于整颗树来说一定是剪了p-1根树枝的,那么状态转移一定是有上面俩个状态转移过来的。
所以我们对这个树先进行一遍dfs,预处理出其fa和size数组,size数组的含义是其子树中(包括他自己)含有多少个黑苹果。
那么对于节点i来说,我们要分两种情况进行考虑::
①:这个苹果是个黑的,那么所有他的子节点如果为son,f[i][1]一定是有f[son][0]转移过来的,因为包含他自己就代表着他自己没有办法被砍,那这个状态只能是1
②:如果这个苹果是红色的,那就要比较麻烦一点了。。首先对于f[i][0]是其所有子节点f[son][0]+f[son][1],带便这这条枝被砍掉,那么f[son][1]就会变为f[son][0],所以要求和。然后对所有的f[son][0]进行相乘(一个简单的分步乘法原理(⊙﹏⊙)b),这就是f[i][0],sum==f[i][0];
而f[i][1]就为(sum/f[son][0]*f[son][1])的和;因为只有一个是少砍一个的,其他的都是砍满的,方案数相乘。
所以最终的结果就是f[0][1](我是从0开始定义的,而p个苹果必须留一个,所以是1);
除法的时候因为有取模,所以要用逆元处理一下即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 #define mod 1000000007 8 int n,m,num,x,y; 9 int adj[1000001],w[1000001]; 10 struct edge{ 11 int s,t,next; 12 }k[2000002]; 13 int read(){ 14 int sum=0;char ch=getchar(); 15 while(ch<'0'||ch>'9') ch=getchar(); 16 while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} 17 return sum; 18 } 19 void init(int s,int t){ 20 k[num].s=s;k[num].t=t; 21 k[num].next=adj[s];adj[s]=num++; 22 k[num].s=t;k[num].t=s; 23 k[num].next=adj[t];adj[t]=num++; 24 } 25 int f[200002][20]; 26 int fa[200002],size[200002]; 27 long long ks(long long p,int n){ 28 long long sq=1; 29 while(n){ 30 if(n&1) sq=sq*p%mod; 31 p=p*p%mod; 32 n>>=1; 33 } 34 return sq; 35 } 36 void dfs(int x){ 37 if(w[x]) size[x]=1; 38 for(int i=adj[x];i!=-1;i=k[i].next){ 39 int o=k[i].t; 40 if(o!=fa[x]){ 41 fa[o]=x; 42 dfs(o); 43 size[x]+=size[o]; 44 } 45 } 46 } 47 void Dp(int x){ 48 long long sum=1; 49 if(!w[x]){ 50 for(int i=adj[x];i!=-1;i=k[i].next){ 51 int o=k[i].t; 52 if(o==fa[x]||!size[o]) continue; 53 Dp(o); 54 f[o][0]+=f[o][1];f[o][0]%=mod; 55 sum*=f[o][0];sum%=mod; 56 } 57 f[x][0]=sum; 58 for(int i=adj[x];i!=-1;i=k[i].next){ 59 int o=k[i].t; 60 if(o==fa[x]||!size[o]) continue; 61 int pl=sum*ks(f[o][0],mod-2)%mod*f[o][1]%mod; 62 f[x][1]+=pl;f[x][1]%=mod; 63 } 64 } 65 else{ 66 for(int i=adj[x];i!=-1;i=k[i].next){ 67 int o=k[i].t; 68 if(o==fa[x]||!size[o]) continue; 69 Dp(o); f[o][0]+=f[o][1];f[o][0]%=mod; 70 sum*=f[o][0];sum%=mod; 71 } 72 f[x][1]=sum; 73 } 74 } 75 int main(){ 76 //freopen("tree.in","r",stdin); 77 //freopen("tree.out","w",stdout); 78 memset(fa,-1,sizeof(fa)); 79 memset(adj,-1,sizeof(adj)); 80 n=read(); 81 for(int i=1;i<n;++i){ 82 x=read(); 83 init(x,i); 84 } 85 for(int i=0;i<n;++i) 86 w[i]=read(); 87 dfs(0);Dp(0); 88 printf("%d ",f[0][1]); 89 return 0; 90 }