牛影传说【线段树+BFS序运用】

题目:

 

牛影村有N个据点,这些据点有N-1条双向道路连接,任意两个据点之间有且仅有一条路径相通。最初每个据点都驻扎着一些牛。神牛H具有法力无边的能力,他随时都可能会降临牛影村的某个据点,每来到一个据点,他可能使用如下三种法力中的一种:

  1、在据点i中选择一头强壮的牛,复制一个一模一样的。(无牛可以继续复制 即直接+1即可)

  2、把据点i及其所有与i有道路连接的据点中的所有牛都复制一分(这些据点的牛数量加倍)。

  3、把据点i及其所有与i有道路连接的据点中的所有牛都带到天宫屠杀

  你作为牛影村的首领,观察统计了神牛H降临牛影村的次数以及他每次施展的法术,现在神牛H再也不会来了,但你想知道神牛为牛影村创造了多少头牛。

 

Input

  第1行是一个整数N,表示牛影村中据点个数,据点分别编号为1..N。  第2行有N个非负整数,依次表示每个据点开始时牛的个数。  接下来的N-1行,每行有两个自然数A和B,表示据点A和据点B之间有一条道路连接。  接下来一个整数M,表示神牛使用法力的次数。  接下来M行,每行以1,2或3开头,代表神牛当前所施展的法力,接着一个自然数i,表示本次法力作用的的据点编号。

Output

  一个整数,表示神牛H创造的牛的数量,这个数可能很大,你需要输出它除以2009的余数即可。

 

Sample Input 1 

3
1 2 3
1 2
1 3
4
2 1
3 2
1 3
2 1

Sample Output 1

14

 

题解

   思考 法力2、3(法力1除了括号里那个坑人的地方 <<原题没有)

       很明显每一个法力能够修改的是不止一个村庄的 

       那么分析这些村庄的联系,  注意题中 牛影村道路 是成一棵树的。

       那么关于任意法力作用点:

            1. 有(唯一)作用点村庄

            2. 有唯一的父亲村庄

            3. 有一系列儿子村庄

        而关于1,2点很明显只能单点修改 (这并不是什么大问题)

       而对于第3点,考虑这一系列儿子村庄的连系

                儿子之间的连系大体存在两种:

                     1.BFS的连系  (节点 儿子具有连续性     即一个节点的所有儿子是排在一起的  但不一定儿子紧挨在父亲后面)

                     2.DFS的连系 (父亲与其儿孙具有连续性  即一个节点的所有子孙都紧挨在其后面)

                这道题我们

                      对于BFS 我们很容易能想到线段树去维护更改(连续序列的维护)

                      而对于DFS 蒟蒻我一筹莫展

        当建立好BFS序之后,简直就是线段树的模板题了

        (但这里因为维护量有两个,所以代码实现是有点容易出错的)

        

主要Code

        BFS序的建立

                涉及变量:

                        q 和 vis  实现树的Bfs遍历

                        Bfs_Time  实现Bfs序列

                        Re(i)    记录村庄 i 在Bfs序中的位置

                        L(i),R(i)   记录村庄 i 的儿子 在Bfs序中所对应连续序列的左右端点

                        Num(i)    记录Bfs序中第i号对应的村庄 

                        以及道路相关变量(全Code里包含相关声明)

void Bfs(){
    queue<int>q;
    bool vis[Nn]={0};
    memset(Pa,0,sizeof(Pa));
    
    vis[1]=1;
    Num[1]=1;
    q.push(1);
    Re[1]=++Bfs_Time;
    while(!q.empty()){
        int t=q.front();q.pop();
        L[Pa[t]]=min(L[Pa[t]],Re[t]);
        R[Pa[t]]=max(R[Pa[t]],Re[t]);    //计算BFS序中L,R <--   先看下面初始化 
        
        for(int i=F[t];i;i=E[i].next){
            int c=E[i].to;
            if(vis[c]) continue ; vis[c]=1;
            Re[c]=++Bfs_Time; Num[Bfs_Time]=c;
            
            Pa[c]=t;
            q.push(c);
            L[t]=Re[c];
            R[t]=Re[c];   //初始化<--     再看上面计算     (Re[c]没有独特意义,只是为了规避干扰)
            
            continue ;
        }
        
        continue ;
    }
    
    return ;
}

 

            线段树相关   (见识某大佬的Code后整合了问题处理 <<Make函数)

int Np=0;
int Root;
struct data{
    int l,r;   
    ll id;    
}G[Nn];       //线段树 节点G 

int Double[Nn];        //标记 数量加倍 (初始化为1,Build函数中初始化) 
int Delet[Nn]={0};    //标记 天宫杀牛 (初始化为0)

void PushData(int now){
    G[now].id=(G[G[now].l].id+G[G[now].r].id)%Mod;
    return ;
}

void Build(int &now,int i,int j){
    now=++Np;
    Double[now]=1;
    
    if(i+1==j){
        G[now].id=(ll)Cow[Num[i]];
        return ;
    }
    
    int mid=i+j>>1;
    Build(G[now].l,i,mid);
    Build(G[now].r,mid,j);
    
    PushData(now);
    return ;
}

void Down(int now){
    if(Delet[now]){
        Delet[now]=0;
        
        G[G[now].l].id=0;
        G[G[now].r].id=0;
        Delet[G[now].l]=1;
        Delet[G[now].r]=1;
        
        Double[now]=1;            //杀牛后翻倍无效,初始化Double 
        return ;
    }
    
    if(Double[now]==1) return ;
    
    G[G[now].l].id=(G[G[now].l].id*Double[now])%Mod;
    G[G[now].r].id=(G[G[now].r].id*Double[now])%Mod;
    Double[G[now].l]=(Double[G[now].l]*Double[now])%Mod;
    Double[G[now].r]=(Double[G[now].r]*Double[now])%Mod;
    
    Double[now]=1;
    return ;
}
ll Make(int now,int i,int j,int x,int y,int op){
    if(now>0)

    if(i>=x && j<=y){
        if(op==1){
            G[now].id++;
            return 1;
        }
        
        if(op==2){
            ll ok=G[now].id;
            G[now].id=(G[now].id*2)%Mod;
            Double[now]=(Double[now]*2)%Mod;
            return ok%Mod;
        }
        
        if(op==3){
            G[now].id=0;
            Delet[now]=1;
            return 0;
        }
    }
    
    int mid=i+j>>1;
    Down(now);
    ll ok=0;
    
    if(mid>=y) ok=Make(G[now].l,i,mid,x,y,op);
    else if(mid<=x) ok=Make(G[now].r,mid,j,x,y,op);
    else ok=Make(G[now].l,i,mid,x,y,op)+Make(G[now].r,mid,j,x,y,op);
    
    PushData(now);
    return ok%Mod;
}

 

 

全Code

   

         

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int Mod=2009; 
const int Nn=200005;
typedef long long ll; 

void Read(int &x){
    x=0;
    char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=x*10-'0'+c,c=getchar();
    
    return ; 
}
void Out(ll x){
    int num=0;
    char s[5005];
    while(x) s[++num]=x%10,x/=10;
    for(int i=num;i>=1;i--) putchar(s[i]+'0');
    putchar('\n');
    
    return ;
}

int N,M;
int Cow[Nn];
int Tot=0;
int F[Nn]={0};
struct alpha{
    int from,to,next;   //储存道路 
}E[Nn];

void Addl(int x,int y){
    E[++Tot]=(alpha){x,y,F[x]};
    F[x]=Tot;
    return ;
}

void Init(){
    Read(N);
    for(int i=1;i<=N;i++)
     Read(Cow[i]);
    for(int i=1;i<N;i++){
        int x,y;
        Read(x),Read(y);
        Addl(x,y);
        Addl(y,x);
    }
    
    return ;
}

ll Sum=0;
int Bfs_Time=0;
int L[Nn],R[Nn],Re[Nn],Pa[Nn],Num[Nn];

void Bfs(){
    queue<int>q;
    bool vis[Nn]={0};
    memset(Pa,0,sizeof(Pa));
    
    vis[1]=1;
    Num[1]=1;
    q.push(1);
    Re[1]=++Bfs_Time;
    while(!q.empty()){
        int t=q.front();q.pop();
        L[Pa[t]]=min(L[Pa[t]],Re[t]);
        R[Pa[t]]=max(R[Pa[t]],Re[t]);    //计算BFS序中L,R <--   先看下面初始化 
        
        for(int i=F[t];i;i=E[i].next){
            int c=E[i].to;
            if(vis[c]) continue ; vis[c]=1;
            Re[c]=++Bfs_Time; Num[Bfs_Time]=c;
            
            Pa[c]=t;
            q.push(c);
            L[t]=Re[c];
            R[t]=Re[c];   //初始化<--     再看上面计算     (Re[c]没有独特意义,只是为了规避干扰)
            
            continue ;
        }
        
        continue ;
    }
    
    return ;
}

int Np=0;
int Root;
struct data{
    int l,r;   
    ll id;    
}G[Nn];       //线段树 节点G 

int Double[Nn];        //标记 数量加倍 (初始化为1,Build函数中初始化) 
int Delet[Nn]={0};    //标记 天宫杀牛 (初始化为0)

void PushData(int now){
    G[now].id=(G[G[now].l].id+G[G[now].r].id)%Mod;
    return ;
}

void Build(int &now,int i,int j){
    now=++Np;
    Double[now]=1;
    
    if(i+1==j){
        G[now].id=(ll)Cow[Num[i]];
        return ;
    }
    
    int mid=i+j>>1;
    Build(G[now].l,i,mid);
    Build(G[now].r,mid,j);
    
    PushData(now);
    return ;
}

void Down(int now){
    if(Delet[now]){
        Delet[now]=0;
        
        G[G[now].l].id=0;
        G[G[now].r].id=0;
        Delet[G[now].l]=1;
        Delet[G[now].r]=1;
        
        Double[now]=1;            //杀牛后翻倍无效,初始化Double 
        return ;
    }
    
    if(Double[now]==1) return ;
    
    G[G[now].l].id=(G[G[now].l].id*Double[now])%Mod;
    G[G[now].r].id=(G[G[now].r].id*Double[now])%Mod;
    Double[G[now].l]=(Double[G[now].l]*Double[now])%Mod;
    Double[G[now].r]=(Double[G[now].r]*Double[now])%Mod;
    
    Double[now]=1;
    return ;
}
ll Make(int now,int i,int j,int x,int y,int op){
    if(now>0)

    if(i>=x && j<=y){
        if(op==1){
            G[now].id++;
            return 1;
        }
        
        if(op==2){
            ll ok=G[now].id;
            G[now].id=(G[now].id*2)%Mod;
            Double[now]=(Double[now]*2)%Mod;
            return ok%Mod;
        }
        
        if(op==3){
            G[now].id=0;
            Delet[now]=1;
            return 0;
        }
    }
    
    int mid=i+j>>1;
    Down(now);
    ll ok=0;
    
    if(mid>=y) ok=Make(G[now].l,i,mid,x,y,op);
    else if(mid<=x) ok=Make(G[now].r,mid,j,x,y,op);
    else ok=Make(G[now].l,i,mid,x,y,op)+Make(G[now].r,mid,j,x,y,op);
    
    PushData(now);
    return ok%Mod;
}

ll Ans=0;
void Solve(){
    Bfs();
    Read(M);
    Build(Root,1,N+1);
    
    for(int i=1;i<=M;i++){
        int x,y;
        Read(x),Read(y);
        Ans=(Ans+Make(Root,1,N+1,Re[y],Re[y]+1,x))%Mod;
    
        if(x!=1){
            if(Pa[y]) 
             Ans=(Ans+Make(Root,1,N+1,Re[Pa[y]],Re[Pa[y]]+1,x))%Mod;
            if(L[y])
             Ans=(Ans+Make(Root,1,N+1,L[y],R[y]+1,x))%Mod;
        }
    }
    
    Out(Ans);
    return ;
}
int main(){
    Init();
    Solve();
    return 0;
}
 

 

posted @ 2018-07-11 19:44  Wannabtl  阅读(555)  评论(0编辑  收藏  举报