【题解】ADAGRAFT - Ada and Graft [SP33331]

【题解】ADAGRAFT - Ada and Graft [SP33331]

传送门:\(\text{Ada and Graft}\) \(\text{[SP33331]}\)

【题目描述】

给出一颗 \(n\) 个节点的树(根为 \(0\)),树的价值定义为每个节点的价值乘积。每个节点的值是其子树中不同颜色种类的数量。

给出每个节点上的颜色,求树价值。答案对 \(10^9+7\) 取模。

【输入】

第一行包含一个整数 \(n\)

第二行包含 \(n-1\) 个整数 \(p_{i}\),分别表示节点 \(i\) 的父亲节点。

第三行包含 \(n\) 个整数 \(F_{i}\),表示节点 \(i\) 的颜色。

【输出】

输出一个整数表示答案模 \(10^9+7\) 的值。

【样例】

样例输入1:
5
0 0 1 1
1 1 1 2 2

样例输出1:
4

样例输入2:
4
0 1 2
6 7 2 3

样例输出2:
24

样例输入3
11
0 1 1 1 3 5 2 7 5 4
494052753 959648710 959648710 959648710 494052753 959648710 959648710 959648710 959648710 494052753 959648710

样例输出3
32

【数据范围】

\(100\%\) \(1 \leqslant N \leqslant 4*10^5,\) \(1 \leqslant F_{i} \leqslant 10^9,\) \(0 \leqslant p_{i} \leqslant i\)


【分析】

求以某个节点为根的子树信息,很明显的线段树合并。

考虑将所有点的权值离散化一下,对每个节点开一颗动态开点权值线段树,按照 \(dfs\) 的遍历顺序不断合并所有子节点,便可得到所有节点的价值,最后乘起来即可。

合并两个权值线段树时可以直接取 \(or\),向上传递信息直接 \(S[p]=S[pl]+S[pr]\)

注意取模!!!

【Code】

#include<algorithm>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=4e5+3,P=1e9+7;
int n,m,x,y,o,Ans=1,A[N],B[N],pt[N],ans[N],head[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
    Re fu=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=fu?-x:x;
}
struct Segment_Tree{
    #define pl tr[p].lp
    #define pr tr[p].rp
    #define mid (L+R>>1)
    int cnt;
    struct QAQ{int S,lp,rp;}tr[N*38];//空间要开够 
    inline void pushup(Re p){tr[p].S=tr[pl].S+tr[pr].S;}
    inline void change(Re &p,Re L,Re R,Re w){
        if(!p)p=++cnt;
        if(L==R){tr[p].S=1;return;}
        if(w<=mid)change(pl,L,mid,w);
        else change(pr,mid+1,R,w);
        pushup(p);
    }
    inline int merge(Re p,Re q,Re L,Re R){
        if(!p)return q;if(!q)return p;
        if(L==R){tr[p].S|=tr[q].S;return p;}//合并信息 
        pl=merge(pl,tr[q].lp,L,mid);
        pr=merge(pr,tr[q].rp,mid+1,R);
        pushup(p);return p;
    }
}T1;
inline void dfs(Re x,Re fa){
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa)
            dfs(to,x),pt[x]=T1.merge(pt[x],pt[to],1,m);
    ans[x]=T1.tr[pt[x]].S;//直接获取整个大区间的答案 
}
int main(){
//    freopen("123.txt","r",stdin);
    in(n);
    for(Re i=2;i<=n;++i)in(x),add(++x,i),add(i,x);
    for(Re i=1;i<=n;++i)in(A[i]),B[i]=A[i];
    sort(B+1,B+n+1),m=unique(B+1,B+n+1)-B-1;
    for(Re i=1;i<=n;++i)T1.change(pt[i],1,m,lower_bound(B+1,B+m+1,A[i])-B);//初始化建树 
    dfs(1,0);
    for(Re i=1;i<=n;++i)Ans=(LL)Ans*ans[i]%P;
    printf("%d\n",Ans);
}
posted @ 2019-11-11 18:19  辰星凌  阅读(270)  评论(0编辑  收藏  举报