[繁华模拟赛]Evensgn 剪树枝

Evensgn 剪树枝

题目

繁华中学有一棵苹果树。苹果树有 n 个节点(也就是苹果),n − 1 条边(也就

是树枝)。调皮的 Evensgn 爬到苹果树上。他发现这棵苹果树上的苹果有两种:一

种是黑苹果,一种是红苹果。Evensgn 想要剪掉 k 条树枝,将整棵树分成 k + 1 个

部分。他想要保证每个部分里面有且仅有一个黑苹果。请问他一共有多少种剪树枝

的方案?

INPUT

第一行一个数字 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 号节点是红苹果。

OUTPUT

输出一个数字,表示总方案数。答案对$10^{9}+7$取模

SAMPLE

INPUT

10

0 1 2 1 4 4 4 0 8

0 0 0 1 0 1 1 0 0 1

OUTPUT

27

解题报告

考试就没咋打这题,于是就骗了十分

正解:

树规

我们设$has[i]$表示节点$i$及其子树带一个黑苹果对父节点产生的方案数贡献

$no[i]$表示节点$i$及其子树不带黑苹果对父节点产生的方案数贡献

我们从叶节点开始讨论

当该叶子为红苹果时,显然无法对父节点产生带黑苹果的方案数贡献,而且它是叶子节点,所以可以对父节点产生$1$的不带黑苹果的方案数贡献,故:

$$has[i]=1,no[i]=0$$

当该节点为黑苹果时,显然可以对父节点产生带黑苹果的方案数贡献,而且它是叶子节点,所以可以对父节点产生$1$的带一个黑苹果的方案数贡献。我们再考虑,当我们砍断它与父节点之间的边时,它也可以对父节点产生$1$的不带黑苹果的方案数贡献,故:

$$has[i]=1,no[i]=1$$

当该节点不为叶子节点时:

假如该节点为黑苹果,那么显然,它的子树无法对他产生带一个黑苹果的方案数贡献(我们先假设它的子树可以,那么,以该节点为根节点的子树就一定有两个或以上的黑苹果,显然是不合法的方案,故舍去)

所以,它的子树对它只能产生不带黑苹果的方案数贡献。

由乘法原理:

$$\prod^{}_{j\in son[i]}no[j]$$

并且,由与黑苹果叶子节点同样的推理方式,我们可以得到:

$$no[i]=has[i]$$

原因仍然是砍断它与父节点之间的边所能造成的贡献

假如该节点为红苹果,我们先考虑$has[i]$,因为它是一个红苹果,所以它自己不能产生对父节点带一个黑苹果的贡献,所以,我们要对子树的$has[j]$进行处理,我们考虑,当该节点的某一棵子树产生了一个黑苹果的贡献时,其它子树就不能再贡献黑苹果了,所以,我们枚举每一棵子树的$has$值,与其它子树的$no$值相乘(同样是乘法原理)(注意这里,实际上有赌数据的成分,假如数据给个菊花树,瞬间爆炸,菊花树:除了根节点,其他点深度均为1,即根节点为所有其他点的父节点的树的简称,然而还是跑的贼快2333),再取和即为其值,即:

$$has[i]=\sum ^{}_{j\in son[i]}(has[i]\times \prod^{}_{k\in son[i] k\neq j}no[k])$$

再考虑$no$值就很简单了,首先,假如它的子树都不产生黑苹果贡献,那么它自然不可以对父节点产生贡献,所以首先取个累乘,接着,假如它的子树产生了黑苹果贡献,只要我们砍掉它与父节点之间的边,它仍然可以产生该贡献,所以:

$$no[i]=\prod^{}_{j\in son[i]}no[j]+has[i]$$

代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 inline int read(){
 6     int sum(0);
 7     char ch(getchar());
 8     for(;ch<'0'||ch>'9';ch=getchar());
 9     for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar());
10     return sum;
11 }
12 const int mod=1000000007;
13 struct edge{
14     int e,n;
15 }a[200005];
16 int pre[100005],tot;
17 inline void insert(int s,int e){
18     a[++tot].e=e;
19     a[tot].n=pre[s];
20     pre[s]=tot;
21 }
22 typedef long long L;
23 int fa[100005];
24 int n;
25 int w[100005];
26 L has[100005],no[100005];
27 inline void dfs(int u){
28     bool flag(false);
29     for(int i=pre[u];i!=-1;i=a[i].n){
30         int e(a[i].e);
31         if(e!=fa[u]){
32             fa[e]=u;
33             flag=true;
34             dfs(e);
35         }
36     }
37     if(!flag){
38         if(w[u]==0){
39             has[u]=0;
40             no[u]=1;
41             return;
42         }
43         has[u]=no[u]=1;
44         return;
45     }
46     if(w[u]==1){
47         has[u]=no[u]=1;
48         for(int i=pre[u];i!=-1;i=a[i].n){
49             int e(a[i].e);
50             if(e!=fa[u])
51                 has[u]=no[u]=(no[u]*no[e])%mod;
52         }
53         return;
54     }
55     has[u]=0;
56     for(int i=pre[u];i!=-1;i=a[i].n){
57         int e1(a[i].e);
58         if(e1==fa[u])
59             continue;
60         L tmp(has[e1]);
61         for(int j=pre[u];j!=-1;j=a[j].n){
62             int e2(a[j].e);
63             if(e1==e2||e2==fa[u])
64                 continue;
65             tmp=(tmp*no[e2])%mod;
66         }
67         has[u]=(has[u]+tmp)%mod;
68     }
69     no[u]=1;
70     for(int i=pre[u];i!=-1;i=a[i].n){
71         int e(a[i].e);
72         if(e==fa[u])
73             continue;
74         no[u]=(no[u]*no[e])%mod;
75     }
76     no[u]=(no[u]+has[u])%mod;
77 }
78 int main(){
79     memset(fa,-1,sizeof(fa));
80     memset(pre,-1,sizeof(pre));
81     n=read();
82     for(int i=1;i<n;i++){
83         int x(read());
84         insert(x,i),insert(i,x);
85     }
86     for(int i=0;i<n;i++)
87         w[i]=read();
88     dfs(0);
89     printf("%lld",has[0]);
90 }
View Code

 

转载于:https://www.cnblogs.com/hzoi-mafia/p/7341265.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值