【洛谷P3380】【模板】二逼平衡樹(樹套樹)
阿新 • • 發佈:2021-01-03
題目
題目連結:https://www.luogu.com.cn/problem/P3380
您需要寫一種資料結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:
- 查詢 \(k\) 在區間內的排名。
- 查詢區間內排名為 \(k\) 的值。
- 修改某一位值上的數值。
- 查詢 \(k\) 在區間內的前驅(前驅定義為嚴格小於 \(x\),且最大的數,若不存在輸出 \(-2147483647\))。
- 查詢 \(k\) 在區間內的後繼(後繼定義為嚴格大於 \(x\),且最小的數,若不存在輸出 \(2147483647\))。
思路
終於有一次寫平衡樹除錯時間不超過 1h 了 /kk。
一般的二逼平衡樹複雜度是 \(O(m\log^3 n)\)
但是我們知道有一些二分是可以直接放線上段樹上的,這樣可以去掉一個 \(\log n\)。但是由於操作二並不是一個字首的詢問,所以沒有辦法放到線段樹上。
但是我們發現,排名為 \(k\) 是一個字首且支援二分,所以我們考慮換一種思路,我們用線段樹維護 val,平衡樹維護 key。
也就是線段樹上區間 \([l,r]\) 的平衡樹表示的是數值在 \([l,r]\) 的位置的下標有哪些。顯然一個下標只會被 \(O(\log n)\) 個區間覆蓋。所以空間複雜度是 \(O(n\log n)\)。
接下來對於每一個操作:
- 直接查詢數值在 \([1,k]\) 中有多少個下標在 \([l,r]\)
- 線上段樹上二分數值,每次查詢左子樹有多少個下標在 \([l,r]\) 內,選擇往左還是往右。
- 將所有包含這個下標的數值區間所對應平衡樹內刪掉這個下標,然後插入新的下標。為了省空間,我寫了一個垃圾回收。
- 先查詢 \(k-1\) 在區間內的排名 \(rk\),然後查詢排名為 \(rk\) 的數值。
- 先查詢 \(k\) 在區間內的排名 \(rk\),然後查詢排名為 \(rk+1\) 的數值。
注意詢問之前要把所有包含數值的操作離散化。
時空複雜度均為 \(O(m\log^2 n)\)。
程式碼
4.5Kb。比我想象的長一些。
// I LOVE DATA STRUCTURE!!! #include <bits/stdc++.h> using namespace std; const int N=100010,LG=18,Inf=2147483647; int n,m,cnt,orz,a[N],b[N]; struct Query { int l,r,opt,k; }ask[N]; struct Treap { int lc[N*LG],rc[N*LG],val[N*LG],size[N*LG],dat[N*LG]; queue<int> q; Treap() { while (q.size()) q.pop(); for (int i=1;i<N*LG;i++) q.push(i); } int New(int v) { int x=q.front(); q.pop(); lc[x]=rc[x]=0; size[x]=1; val[x]=v; dat[x]=rand(); return x; } void pushup(int x) { size[x]=size[lc[x]]+size[rc[x]]+1; } void zig(int &x) { int y=lc[x],z=rc[y]; rc[y]=x; lc[x]=z; x=y; pushup(rc[x]); pushup(x); } void zag(int &x) { int y=rc[x],z=lc[y]; lc[y]=x; rc[x]=z; x=y; pushup(lc[x]); pushup(x); } int build() { int x=New(Inf),y=New(-Inf); lc[x]=y; size[x]=2; return x; } int ins(int x,int v) { if (!x) x=New(v); else if (val[x]>v) { lc[x]=ins(lc[x],v); if (dat[lc[x]]>dat[x]) zig(x); } else { rc[x]=ins(rc[x],v); if (dat[rc[x]]>dat[x]) zag(x); } pushup(x); return x; } int del(int x,int v) { if (val[x]==v) { if (!lc[x] && !rc[x]) { q.push(x); return 0; } if (!rc[x] || (lc[x] && dat[rc[x]]<dat[lc[x]])) zig(x),rc[x]=del(rc[x],v); else zag(x),lc[x]=del(lc[x],v); } else if (val[x]>v) { lc[x]=del(lc[x],v); if (dat[lc[x]]>dat[x]) zig(x); } else { rc[x]=del(rc[x],v); if (dat[rc[x]]>dat[x]) zag(x); } pushup(x); return x; } int getrk(int x,int v) { if (!x) return 0; if (val[x]==v) return size[lc[x]]; if (val[x]>v) return getrk(lc[x],v); if (val[x]<v) return size[lc[x]]+1+getrk(rc[x],v); return 23333; } void debug(int x) { if (lc[x]) printf("%d %d\n",val[x],val[lc[x]]); if (rc[x]) printf("%d %d\n",val[x],val[rc[x]]); if (lc[x]) debug(lc[x]); if (rc[x]) debug(rc[x]); } }treap; struct SegTree { int rt[N*4]; void build(int x,int l,int r) { rt[x]=treap.build(); if (l==r) return; int mid=(l+r)>>1; build(x*2,l,mid); build(x*2+1,mid+1,r); } void update(int x,int l,int r,int k,int v,bool tag) { if (tag) rt[x]=treap.ins(rt[x],v); else rt[x]=treap.del(rt[x],v); if (l==r) return; int mid=(l+r)>>1; if (k<=mid) update(x*2,l,mid,k,v,tag); else update(x*2+1,mid+1,r,k,v,tag); } int query1(int x,int l,int r,int ql,int qr,int pl,int pr) { if (ql<=l && r<=qr) return treap.getrk(rt[x],pr+1)-treap.getrk(rt[x],pl); int mid=(l+r)>>1; int sum=0; if (ql<=mid) sum+=query1(x*2,l,mid,ql,qr,pl,pr); if (qr>mid) sum+=query1(x*2+1,mid+1,r,ql,qr,pl,pr); return sum; } int query2(int x,int l,int r,int ql,int qr,int k) { if (l==r) return l; int mid=(l+r)>>1; int cnt=treap.getrk(rt[x*2],qr+1)-treap.getrk(rt[x*2],ql); if (k<=cnt) return query2(x*2,l,mid,ql,qr,k); else return query2(x*2+1,mid+1,r,ql,qr,k-cnt); } }seg; int main() { srand(53962); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&a[i]); b[++cnt]=a[i]; } for (int i=1;i<=m;i++) { scanf("%d",&ask[i].opt); if (ask[i].opt==3) { scanf("%d%d",&ask[i].l,&ask[i].k); b[++cnt]=ask[i].k; } else scanf("%d%d%d",&ask[i].l,&ask[i].r,&ask[i].k); } sort(b+1,b+1+cnt); cnt=unique(b+1,b+1+cnt)-b-1; seg.build(1,1,cnt); for (int i=1;i<=n;i++) { a[i]=lower_bound(b+1,b+1+cnt,a[i])-b; seg.update(1,1,cnt,a[i],i,1); } for (int i=1;i<=m;i++) { if (ask[i].opt==1) { int k=upper_bound(b+1,b+1+cnt,ask[i].k-1)-b-1; printf("%d\n",seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r)+1); } if (ask[i].opt==2) printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,ask[i].k)]); if (ask[i].opt==3) { int k=lower_bound(b+1,b+1+cnt,ask[i].k)-b; seg.update(1,1,cnt,a[ask[i].l],ask[i].l,0); seg.update(1,1,cnt,k,ask[i].l,1); a[ask[i].l]=k; } if (ask[i].opt==4) { int k=upper_bound(b+1,b+1+cnt,ask[i].k-1)-b-1; if (k==0) { printf("%d\n",-Inf); continue; } int rk=seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r); if (rk==0) { printf("%d\n",-Inf); continue; } printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,rk)]); } if (ask[i].opt==5) { int k=upper_bound(b+1,b+1+cnt,ask[i].k)-b-1; int rk=seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r)+1; if (rk>ask[i].r-ask[i].l+1) { printf("%d\n",Inf); continue; } printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,rk)]); } } return 0; }