【洛谷P3835】【模板】可持久化平衡樹
阿新 • • 發佈:2021-01-20
題目
題目連結:https://www.luogu.com.cn/problem/P3835
您需要寫一種資料結構(可參考題目標題),來維護一個可重整數集合,其中需要提供以下操作( 對於各個以往的歷史版本 ):
- 插入 \(x\)
- 刪除 \(x\)(若有多個相同的數,應只刪除一個,如果沒有請忽略該操作)
- 查詢 \(x\) 的排名(排名定義為比當前數小的數的個數 \(+1\))
- 查詢排名為 \(x\) 的數
- 求 \(x\) 的前驅(前驅定義為小於 \(x\),且最大的數,如不存在輸出 \(-2^{31}+1\) )
- 求 \(x\) 的後繼(後繼定義為大於 \(x\),且最小的數,如不存在輸出 \(2^{31}-1\)
和原本平衡樹不同的一點是,每一次的任何操作都是基於某一個歷史版本,同時生成一個新的版本。(操作3, 4, 5, 6即保持原版本無變化)
每個版本的編號即為操作的序號(版本0即為初始狀態,空樹)
\(n\leq 5\times 10^5,|x|\leq 10^9\)。
思路
直接上 FHQ Treap 即可。注意每次 split 和 merge 時都要複製一份節點。
注意因為 FHQ Treap 是期望深度 \(\log n\) 的,所以節點數量不能開的太死,我開的是 \(50\) 倍。
時間複雜度 \(O(n\log n)\)。
程式碼
#include <bits/stdc++.h> using namespace std; const int N=500010,MAXN=N*50,Inf=2147483647; int n,rt[N]; struct FHQ { int tot,ch[MAXN][2],val[MAXN],dat[MAXN],siz[MAXN]; void pushup(int x) { siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1; } int New(int v) { int x=++tot; val[x]=v; dat[x]=rand(); siz[x]=1; return x; } int cpynode(int x) { int y=++tot; ch[y][0]=ch[x][0]; ch[y][1]=ch[x][1]; val[y]=val[x]; dat[y]=dat[x]; siz[y]=siz[x]; return y; } void build() { int x=New(Inf),y=New(-Inf); ch[x][0]=y; pushup(x); rt[0]=x; } void split(int x,int k,int &lc,int &rc) { if (!x) { lc=rc=0; return; } int y=cpynode(x); if (val[y]<=k) lc=y,split(ch[y][1],k,ch[y][1],rc); else rc=y,split(ch[y][0],k,lc,ch[y][0]); pushup(y); } int merge(int x,int y) { if (!x || !y) return x|y; if (dat[x]>dat[y]) { int z=cpynode(x); ch[z][1]=merge(ch[z][1],y); pushup(z); return z; } else { int z=cpynode(y); ch[z][0]=merge(x,ch[z][0]); pushup(z); return z; } } void ins(int &root,int v) { int x,y; split(root,v,x,y); root=merge(merge(x,New(v)),y); } void del(int &root,int v) { int x,y,z; split(root,v,x,y); split(x,v-1,x,z); if (val[z]==v) z=merge(ch[z][0],ch[z][1]); root=merge(merge(x,z),y); } int getrk(int &root,int v) { int x,y,z; split(root,v-1,x,y); z=siz[x]; root=merge(x,y); return z; } int getval(int x,int k) { if (siz[ch[x][0]]+1==k) return val[x]; if (siz[ch[x][0]]>=k) return getval(ch[x][0],k); else return getval(ch[x][1],k-siz[ch[x][0]]-1); } int pre(int &root,int v) { int x,y,z; split(root,v-1,x,y); z=getval(x,siz[x]); root=merge(x,y); return z; } int nxt(int &root,int v) { int x,y,z; split(root,v,x,y); z=getval(y,1); root=merge(x,y); return z; } }fhq; int main() { srand(1023); scanf("%d",&n); fhq.build(); for (int i=1;i<=n;i++) { int opt,now,x; scanf("%d%d%d",&now,&opt,&x); rt[i]=rt[now]; if (opt==1) fhq.ins(rt[i],x); if (opt==2) fhq.del(rt[i],x); if (opt==3) printf("%d\n",fhq.getrk(rt[i],x)); if (opt==4) printf("%d\n",fhq.getval(rt[i],x+1)); if (opt==5) printf("%d\n",fhq.pre(rt[i],x)); if (opt==6) printf("%d\n",fhq.nxt(rt[i],x)); } return 0; }