zoukankan      html  css  js  c++  java
  • 牛影传说【线段树+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('
    ');
        
        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;
    }
     
  • 相关阅读:
    新模板电子版发布
    FZU 1686 神龙的难题 (重复覆盖)
    HDU 2295 Radar (重复覆盖)
    ZOJ 3209 Treasure Map (Dancing Links)
    HUST 1017
    《微信小程序商城开发实战》唐磊,全网真实评价截图,不吹不黑,全部来自网友的真实评价
    《微信小程序商城开发实战》笔者的新书,欢迎各位粉丝上京东购买
    ***ThinkPHP中的常用方法汇总总结:M方法,D方法,U方法,I方法
    ***微信小程序学习文档和资料归档收集
    ***OneinStack交互安装FAQ和管理服务常用命令
  • 原文地址:https://www.cnblogs.com/Aloyd/p/9296376.html
Copyright © 2011-2022 走看看