树规 Evensgn 剪树枝

    

问题 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 取模。

样例输入

样例输入 260 1 1 0 41 1 0 0 1 0样例输入 3100 1 2 1 4 4 4 0 80 0 0 1 0 1 1 0 0 1

样例输出

样例输出 12样例输出 21样例输出 327

提示

数据范围

对于 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% 中各有一个点,树的形态是一条链。

     明显的树规。联考时AC

    has[i]表示i节点能提供给父亲连有一个黑苹果的方案数。

    no[i]表示i节点能提供给父亲(不提供黑苹果)的方案数。

    我写的状态数组貌似很麻烦。。很难理解。。但效率NO.1

    对于叶子节点,

         是黑苹果:has=1,no=1(因为自己与父亲连的枝可以砍掉,所以no=1)

         是红苹果:has=0,no=1

    是非叶子节点,

         是红苹果:no[i]= π no[son]+has[i]。这个好理解吧,加上has是因为砍去和父亲连边相当于贡献了has个no。

                           has[i]=Σ  has[j]*  π no[son](这里的son刨去了j)j是每一个儿子,因为i本身无黑苹果,所以他想有一个,必来自某一个儿子。

         是黑苹果:has=π no[son]   他自己是黑苹果,子不可能再传上来黑苹果。

                           no=has   理同上。

         最后输出has[0].(π指累乘)

   

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100000
#define mod 1000000007
#define ll long long
using namespace std;
int read()
{
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
    while(x>='0'&&x<='9'){sum=sum*10+x-'0';x=getchar();}
    return sum*f;
}
struct node
{
    int v,next;
} lu[N*2+5];
int n,a[N+5],adj[N+5],e; 
ll has[N+5],no[N+5];
void add(int u,int v){lu[++e].v=v;lu[e].next=adj[u];adj[u]=e;}
void dfs(int x,int fa)
{
    int p=0;
    for(int i=adj[x];i;i=lu[i].next)
       if(lu[i].v!=fa)
           dfs(lu[i].v,x),p=1;
    if(!p){
        if(a[x]==1){no[x]=1;has[x]=1;return;}
        else{ has[x]=0;no[x]=1;return;}
    }
    ll sum=1;
    if(a[x]==1){
        for(int i=adj[x];i;i=lu[i].next){
            int to=lu[i].v;
            if(to!=fa)
               sum=(sum*no[to])%mod;
        }
        no[x]=has[x]=sum;
        return;
    }
    else{
        for(int i=adj[x];i;i=lu[i].next){
            int to=lu[i].v;
            if(to!=fa)sum=(sum*no[to])%mod;
        }
        no[x]=sum;
        for(int i=adj[x];i;i=lu[i].next){
            int to=lu[i].v;
            if(to!=fa){
               sum=has[to];
               for(int i=adj[x];i;i=lu[i].next)
                    if(lu[i].v!=fa&&lu[i].v!=to)
                        sum=(sum*no[lu[i].v])%mod;
                has[x]=(has[x]+sum)%mod;
            }
        }
        no[x]=(no[x]+has[x])%mod;
        return;
    }
}
int main()
{
    n=read();int x;
    for(int i=1;i<n;i++)x=read(),add(i,x),add(x,i);
    for(int i=0;i<n;i++)a[i]=read();
    dfs(0,0);
    printf("%lld\n",has[0]%mod);
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值