[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\),那麼根據尤拉序的性質有:
- \(x\)到根路徑查詢:直接查詢\([1,l_x]\)的和即可
- \(x\)子樹加\(v\):區間\([l_x,r_x]\)加\(v\),注意由於區間的點符號可能為正或負,要記錄所有符號(\(+1,-1\))的和\(t\),那麼綜合增加的值就是\(v\cdot t\)
- 將\(x\)父親換為\(fa\):相當於把區間\([l_x,r_x]\)
顯然可以用非旋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); } } }