zoukankan      html  css  js  c++  java
  • [BZOJ3786] 星系探索(括号序列+Splay)

    3786: 星系探索

    Time Limit: 40 Sec  Memory Limit: 256 MB
    Submit: 2191  Solved: 644
    [Submit][Status][Discuss]

    Description

    物理学家小C的研究正遇到某个瓶颈。

    他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球。主星球没有依赖星球。

    我们定义依赖关系如下:若星球a的依赖星球是b,则有星球a依赖星球b.此外,依赖关系具有传递性,即若星球a依赖星球b,星球b依赖星球c,则有星球a依赖星球c.

    对于这个神秘的星系中,小C初步探究了它的性质,发现星球之间的依赖关系是无环的。并且从星球a出发只能直接到达它的依赖星球b.

    每个星球i都有一个能量系数wi.小C想进行若干次实验,第i次实验,他将从飞船上向星球di发射一个初始能量为0的能量收集器,能量收集器会从星球di开始前往主星球,并收集沿途每个星球的部分能量,收集能量的多少等于这个星球的能量系数。

    但是星系的构成并不是一成不变的,某些时刻,星系可能由于某些复杂的原因发生变化。

    有些时刻,某个星球能量激发,将使得所有依赖于它的星球以及他自己的能量系数均增加一个定值。还有可能在某些时刻,某个星球的依赖星球会发生变化,但变化后依然满足依赖关系是无环的。

    现在小C已经测定了时刻0时每个星球的能量系数,以及每个星球(除了主星球之外)的依赖星球。接下来的m个时刻,每个时刻都会发生一些事件。其中小C可能会进行若干次实验,对于他的每一次实验,请你告诉他这一次实验能量收集器的最终能量是多少。

    Input

    第一行一个整数n,表示星系的星球数。

    接下来n-1行每行一个整数,分别表示星球2-n的依赖星球编号。

    接下来一行n个整数,表示每个星球在时刻0时的初始能量系数wi.

    接下来一行一个整数m,表示事件的总数。

    事件分为以下三种类型。

    (1)"Q di"表示小C要开始一次实验,收集器的初始位置在星球di.

    (2)"C xi yi"表示星球xi的依赖星球变为了星球yi.

    (3)"F pi qi"表示星球pi能量激发,常数为qi.

    Output

    对于每一个事件类型为Q的事件,输出一行一个整数,表示此次实验的收集器最终能量。

    Sample Input

    3
    1
    1
    4 5 7
    5
    Q 2
    F 1 3
    Q 2
    C 2 3
    Q 2

    Sample Output

    9
    15
    25

    HINT

    n<=100000,m<=300000,1<di,xi<=n,wi,qi<=100000.保证操作合法。注意w_i>=0

    Solution

       这是jly dalao课上讲过的例题,所以我下来实现一下。

      考虑维护原树的括号序列,设一个节点x的进入和离开的时间戳为l[x]和r[x],那么对于

      操作C:把x的父亲节点变为y,相当于把区间[l[x],r[x]]插入到l[y]之后;

      操作Q:查询节点x到根的路径的权值和,相当于查询区间[l[1],l[x]]中所有只出现了一次的节点的权值和,因为显然在括号序列中一个位置p的前缀中所有只出现了一次的节点就是它的所有祖先

      操作F:把x的子树整体加v,相当于区间[l[x],r[x]]整体加v。

      所以用一个Splay维护括号序列,支持区间删除插入、区间加和区间查询就可以了。

      但还有一个问题:如何实现只查询出现了一次的节点的权值和?

      对每个节点x的l[x]打上正标记,r[x]打上负标记即可。

      注意这题要开longlong,括号序列长度为2*n不用我说吧,并且有点卡时(看看时限就懂了),请使用较优秀的IO并且不要用STL。。。

      写给自己的调Splay的注意事项:

    1. Splay的基本结构:检查父子关系是否成功构建,是否赋初值,sz值是否正常。(RE)
    2. pushdown是写在kth还是splay中:如果是后者,需要用一个把祖先push进去,然后一个个pushdown。
    3. 检查pushup和change_val是否写对:这一点要根据具体题目要维护的信息。(WA)

      这题码了0.5h,然后其实只有两个很小的错误,一个在第一步中de了0.5h,另一个在第三步de了10min,还要加油啊。  

    Code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=1e5+5,INF=0x3f3f3f3f;
    inline int read(){
        int x=0,w=0;char ch=0;
        while(!isdigit(ch)) w|=ch=='-',ch=getchar();
        while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    bool b[N<<1];
    int n,m,idx,l[N],r[N],a[N<<1],w[N],st[N<<1],tp;
    vector<int> to[N];
    void dfs(int x){
        l[x]=++idx;a[idx]=w[x];b[idx]=0;
        for(int i=0;i<to[x].size();++i) dfs(to[x][i]);
        r[x]=++idx;a[idx]=-w[x];b[idx]=1;
    }
    struct Splay{
        struct Node{
            int fa,ch[2];
            bool fg;
            LL val,add,sum,c1,c2;//c1:fg为0的节点数      c2:fg为1的节点数 
            Node(){}
            Node(int a,int b,int f,int c,bool d):fa(f),val(c),fg(d){ch[0]=a,ch[1]=b;c1=c2=add=sum=0;}
        }t[N<<1];
        int rt;
        int get(int x){return x==t[t[x].fa].ch[1];}
        void link(int x,int y,int d){t[x].ch[d]=y;t[y].fa=x;}
        void change_val(int x,LL v){
            t[x].val+=t[x].fg?-v:v;
            t[x].sum+=(t[x].c1-t[x].c2)*v;
            t[x].add+=v;
        }
        void pushup(int x){
            int &l=t[x].ch[0],&r=t[x].ch[1];
            t[x].sum=t[l].sum+t[r].sum+t[x].val;
            t[x].c1=t[l].c1+t[r].c1+(t[x].fg==0);
            t[x].c2=t[l].c2+t[r].c2+(t[x].fg==1);
        }
        void pushdown(int x){
            if(t[x].add){
                change_val(t[x].ch[0],t[x].add);
                change_val(t[x].ch[1],t[x].add);
                t[x].add=0;
            }
        }
        void rotate(int x){
            int d=get(x),f=t[x].fa,ff=t[f].fa;
            link(ff,x,get(f));
            link(f,t[x].ch[d^1],d);
            link(x,f,d^1);
            pushup(f),pushup(x);
        }
        void splay(int x,int goal){
            tp=0;
            for(int i=x;i;i=t[i].fa) st[++tp]=i;
            while(tp) pushdown(st[tp--]);
            for(;t[x].fa^goal;rotate(x)){
                int f=t[x].fa,ff=t[f].fa;
                if(ff^goal) get(x)^get(f)?rotate(x):rotate(f);
            }
            if(!goal) rt=x;
        }
        int build(int l,int r,int f){
            if(l>r) return 0;
            int mid=l+r>>1;
            t[mid]=Node(build(l,mid-1,mid),build(mid+1,r,mid),f,a[mid],b[mid]);
            pushup(mid);
            return mid;
        }
        int next(int x,int d){
            splay(x,0);
            x=t[x].ch[d];
            while(t[x].ch[d^1]) x=t[x].ch[d^1];
            return x;
        }
        int split(int i,int j){
            splay(i,0),splay(j,i);return t[j].ch[0];
        }
        void update(int a,int v){
            int i=next(l[a],0),j=next(r[a],1);
            int x=split(i,j);
            change_val(x,v);
            pushup(j),pushup(i);
        }
        void change(int a,int b){
            int i=next(l[a],0),j=next(r[a],1);
            int x=split(i,j);
            t[j].ch[0]=0;
            pushup(j),pushup(i);
            i=l[b],j=next(l[b],1);
            split(i,j);
            link(j,x,0);
            pushup(j),pushup(i);
        }
        LL query(int a){
            return t[split(next(l[1],0),next(l[a],1))].sum;
        }
    }T;
    int main(){
        n=read();
        for(int i=2;i<=n;++i) to[read()].push_back(i);
        for(int i=1;i<=n;++i) w[i]=read();
        a[++idx]=0; //哨兵的b值没有影响 
        dfs(1);
        a[++idx]=0;
        T.rt=T.build(1,n*2+2,0);
        char ch[2];
        m=read();
        while(m--){
            scanf("%s",ch);int x=read();
            if(ch[0]=='Q') printf("%lld
    ",T.query(x));
            else if(ch[0]=='C') T.change(x,read());
            else T.update(x,read());
        }
        return 0;
    }
    BZOJ3786
  • 相关阅读:
    活动的生命周期
    活动
    开始编程前的准备工作
    数组转List
    Word根据模板生成数据
    Excel根据模板生成数据
    php取年份区间
    世界 国家 省份 sql
    相册处理,php中获取一组前缀相同的元素值
    mysql添加字段
  • 原文地址:https://www.cnblogs.com/gosick/p/11254197.html
Copyright © 2011-2022 走看看