1. 程式人生 > 實用技巧 >[BZOJ3786]星系探索(尤拉序+非旋treap)

[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\),那麼綜合增加的值就是\(v\cdot t\)
  3. \(x\)父親換為\(fa\):相當於把區間\([l_x,r_x]\)
    接到新的父親的入棧序\(l_{fa}\)後面

顯然可以用非旋Treap實現,這個演算法也被稱為尤拉環遊樹(Euler Tour Tree,ETT).複雜度\(O(n\log 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\n",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);
        }
    }
}