[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);
        }
    }
}
原文地址:https://www.cnblogs.com/birchtree/p/13439233.html