• [BZOJ3786]星系探索(欧拉序+非旋treap)


    [BZOJ3786]星系探索(欧拉序+非旋treap)

    题面、

    物理学家小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可能会进行若干次实验,对于他的每一次实验,请你告诉他这一次实验能量收集器的最终能量是多少。

    分析

    给出一棵根为1的树,要支持:子树加,换父亲,查询节点到根的路径的点权和。由于树是动态的不能树剖,又需要维护路径信息和子树信息,不妨考虑欧拉序。设进栈序为(l_x),出栈序为(r_x).进栈时把值设为(a_x),出栈时设为(-a_x),那么根据欧拉序的性质有:

    1. (x)到根路径查询:直接查询([1,l_x])的和即可
    2. (x)子树加(v):区间([l_x,r_x])(v),注意由于区间的点符号可能为正或负,要记录所有符号((+1,-1))的和(t),那么综合增加的值就是(vcdot t)
    3. (x)父亲换为(fa):相当于把区间([l_x,r_x])接到新的父亲的入栈序(l_{fa})后面

    显然可以用非旋Treap实现,这个算法也被称为欧拉环游树(Euler Tour Tree,ETT).复杂度(O(nlog n))

    需要注意换父亲操作导致欧拉序中节点顺序改变,非旋Treapsplit的时候不能直接split(l[x]),要在树上先求(l_x)现在排在序列的位置(即到根路径上左子树大小+1的和),然后再按这个排名split

    代码

    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #define maxn 500000
    using namespace std;
    typedef long long ll;
    int n,m;
    struct edge {
        int from;
        int to;
        int next;
    } E[maxn*2+5];
    int head[maxn+5];
    int esz=1;
    void add_edge(int u,int v) {
        esz++;
        E[esz].from=u;
        E[esz].to=v;
        E[esz].next=head[u];
        head[u]=esz;
    }
    int tim;
    int lb[maxn+5],rb[maxn+5],a[maxn+5];
    struct fhq_treap {
    #define lson(x) (tree[x].ls)
    #define rson(x) (tree[x].rs)
    #define fa(x) (tree[x].fa)
        struct node {
            int ls;
            int rs;
            int fa;
            int dat;
            int sz;
            int tval;
            ll tsum;//差分标记和
            ll tag;
            ll val;//加上差分标记的值
            ll sum;
        } tree[maxn+5];
        int ptr;
        int root;
        int New(int val,int type) {
            ptr++;
            tree[ptr].sz=1;
            tree[ptr].dat=rand();
            tree[ptr].tsum=tree[ptr].tval=type;
            tree[ptr].sum=tree[ptr].val=val*type;
            return ptr;
        }
        void push_up(int x) {
            tree[x].sz=tree[lson(x)].sz+1+tree[rson(x)].sz;
            tree[x].tsum=tree[x].tval+tree[lson(x)].tsum+tree[rson(x)].tsum;
            tree[x].sum=tree[x].val+tree[lson(x)].sum+tree[rson(x)].sum;
            if(lson(x)) fa(lson(x))=x;
            if(rson(x)) fa(rson(x))=x;
        }
        void add_tag(int x,ll tag) {
            tree[x].val+=tree[x].tval*tag;//因为子树内点可能符号不一样,要乘上标记和 
            tree[x].sum+=tree[x].tsum*tag;
            tree[x].tag+=tag;
        }
        void push_down(int x) {
            if(tree[x].tag) {
                add_tag(lson(x),tree[x].tag);
                add_tag(rson(x),tree[x].tag);
                tree[x].tag=0;
            }
        }
        int merge(int x,int y) {
            push_down(x);
            push_down(y);
            if(!x||!y) return x+y;
            if(tree[x].dat<tree[y].dat) {
                lson(y)=merge(x,lson(y));
                push_up(y);
                return y;
            } else {
                rson(x)=merge(rson(x),y);
                push_up(x);
                return x;
            }
        }
        void split(int now,int k,int &x,int &y) {
            if(now==0) {
                x=y=0;
                return;
            }
            push_down(now);
            if(k<=tree[lson(now)].sz) {
                y=now;
                split(lson(now),k,x,lson(y));
            } else {
                x=now;
                split(rson(now),k-tree[lson(now)].sz-1,rson(x),y);
            }
            push_up(now);
        }
        int get_rank(int x) { //因为换父亲会改变欧拉序对应节点的位置,要查真实排名
            int ans=tree[lson(x)].sz+1;
            while(fa(x)) {
                if(rson(fa(x))==x) ans+=tree[lson(fa(x))].sz+1;
                x=fa(x);
            }
            return ans;
        }
        void add(int id,int val) {
            int x,y,z;
            split(root,get_rank(rb[id]),y,z);
            fa(y)=fa(z)=0;
            split(y,get_rank(lb[id])-1,x,y);//分出id子树对应的区间
            fa(x)=fa(y)=0;
            add_tag(y,val);
            root=merge(merge(x,y),z);
            fa(root)=0;
        }
        ll query(int id) {
            int x,y;
            split(root,get_rank(lb[id]),x,y);
            ll ans=tree[x].sum;
            root=merge(x,y);
            return ans;
        }
        void change_fa(int id,int fa_id) {
            int x,y,z;
            split(root,get_rank(rb[id]),y,z);
            fa(y)=fa(z)=0;
            split(y,get_rank(lb[id])-1,x,y);//提取出原来的子树
            fa(x)=fa(y)=0;
            root=merge(x,z);
            fa(root)=0;
            split(root,get_rank(lb[fa_id]),x,z);//把子树插到新的父亲后面
            root=merge(merge(x,y),z);
            fa(root)=0;
        }
        void build(int x,int f) {
            lb[x]=New(a[x],1);
            root=merge(root,lb[x]);
            for(int i=head[x]; i; i=E[i].next) {
                int y=E[i].to;
                if(y!=f) build(y,x);
            }
            rb[x]=New(a[x],-1);
            root=merge(root,rb[x]);
        }
    } T;
    
    int main() {
        int u,v;
        char op[10];
        scanf("%d",&n);
        for(int i=2; i<=n; i++) {
            scanf("%d",&u);
            add_edge(u,i);
            add_edge(i,u);
        }
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        T.build(1,0);
        scanf("%d",&m);
        for(int i=1; i<=m; i++) {
            scanf("%s",op);
            if(op[0]=='Q') {
                scanf("%d",&u);
                printf("%lld
    ",T.query(u));
            } else if(op[0]=='C') {
                scanf("%d %d",&u,&v);
                T.change_fa(u,v);
            } else {
                scanf("%d %d",&u,&v);
                T.add(u,v);
            }
        }
    }
    
  • 相关阅读:
    寒假学习5流程控制之ifelse与for循环
    寒假学习4scala数据类型与运算符
    寒假学习13
    寒假学习14
    寒假学习3scala简介与基础1
    sqlserver 定时自动备份
    windows mysql 主从同步
    mysql 连接数 最大并发数
    mysql基础语法
    sqlserver 常见语法
  • 原文地址:https://www.cnblogs.com/birchtree/p/13439233.html
Copyright © 2020-2023  润新知