【樹套樹】【BZOJ3196】二逼平衡樹
阿新 • • 發佈:2018-12-30
【題目描述】
您需要寫一種資料結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:
1.查詢 x在區間內的排名;
2.查詢區間內排名為 k 的值;
3.修改某一位置上的數值;
4.查詢 x 在區間內的前趨(前趨定義為小於 x,且最大的數);
5.查詢 x 在區間內的後繼(後繼定義為大於 x,且最小的數)。
【輸入格式】
第一行兩個數 n,m,表示長度為 n 的有序序列和 m 個操作。
第二行有 n個數,表示有序序列。
下面有 m 行,每行第一個數表示操作型別:
1.之後有三個數 l,r,x表示查詢 x在區間 [l,r] 的排名;
2.之後有三個數 l,r,k表示查詢區間 [l,r]內排名為 k的數;
3.之後有兩個數 pos,x表示將 pos位置的數修改為 x;
4.之後有三個數 l,r,x表示查詢區間 [l,r]內 x 的前趨;
5.之後有三個數 l,r,x表示查詢區間 [l,r]內 x 的後繼。
【輸出格式】
對於操作 1,2,4,5各輸出一行,表示查詢結果。
非常經典的樹套樹題目,這裡採用的是線段樹套平衡樹的做法
對於操作一,我們考慮查詢每一分割槽間小於查詢值的數的數量累加起來+1即可,查詢過程只需要將其嚴格次小字首轉到根即可
對於操作二,我們考慮二分一個數,然後參照操作一的做法,查詢所有小於等於二分值的數的數量,可以明白,這是單調遞增的,故二分可行,注意與操作一不一樣的是這裡我們需要將非嚴格次小字首轉到根
對於操作三,直接對每一包含該位置的區間一次刪除,一次插入即可
對於操作四,查詢每一分割槽間的嚴格次小字首,取較大值
對於操作五,查詢每一分割槽間的嚴格次大字尾,取較小值
#include<iostream> #include<iomanip> #include<cmath> #include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; int n,m,a[50005],root[200005],sign,ans,op,sum; struct Tree { int ch[2],fa; int recy,sum; int v; }tree[1650005]; int crepoint(int v,int fa) { sign++; tree[sign].v=v; tree[sign].fa=fa; tree[sign].recy=tree[sign].sum=1; return sign; } int whoson(int x) { return (tree[tree[x].fa].ch[0]==x)?0:1; } void connect(int x,int fa,int son) { tree[x].fa=fa; tree[fa].ch[son]=x; } void push_up(int k) { tree[k].sum=tree[tree[k].ch[0]].sum+tree[tree[k].ch[1]].sum+tree[k].recy; } void rotate(int x) { int y=tree[x].fa; int mroot=tree[y].fa; int yson=whoson(x); int mrootson=whoson(y); int B=tree[x].ch[yson^1]; connect(B,y,yson); connect(y,x,yson^1); connect(x,mroot,mrootson); push_up(y); push_up(x); } void Splay(int x,int to,int k) { while(tree[x].fa!=to) { int y=tree[x].fa,z=tree[y].fa; if(z!=to) { if(whoson(x)^whoson(y)) rotate(x); else rotate(y); } rotate(x); } if(!to) root[k]=x; } void find(int nowroot,int v,int k) { int now=nowroot; while(now) { if(v==tree[now].v) { Splay(now,0,k); return ; } int nex=(v<tree[now].v)?0:1; now=tree[now].ch[nex]; } } void push(int &nowroot,int v,int k) { if(!nowroot) { nowroot=crepoint(v,0); return ; } int now=nowroot; while(now) { tree[now].sum++; if(v==tree[now].v) { tree[now].recy++; Splay(now,0,k); return ; } int nex=(v<tree[now].v)?0:1; if(!tree[now].ch[nex]) { tree[now].ch[nex]=crepoint(v,now); Splay(tree[now].ch[nex],0,k); return ; } now=tree[now].ch[nex]; } } void pop(int &nowroot,int v,int k) { find(nowroot,v,k); if(tree[nowroot].recy>1) { tree[nowroot].recy--; tree[nowroot].sum--; return ; } int ls=tree[nowroot].ch[0],rs=tree[nowroot].ch[1]; if(!ls&&!rs) { nowroot=0; return ; } if(!ls) { nowroot=rs; tree[rs].fa=0; return ; } if(!rs) { nowroot=ls; tree[ls].fa=0; return ; } while(tree[ls].ch[1]) ls=tree[ls].ch[1]; Splay(ls,0,k); tree[nowroot].ch[1]=rs; if(rs) tree[rs].fa=nowroot; } void build(int k,int l,int r) { for(int i=l;i<=r;i++) push(root[k],a[i],k); if(l==r) return ; int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } int super(int nowroot,int v) { int maxx=-0x7fffffff,nows=0; int now=nowroot; while(now) { if(tree[now].v<v) if(maxx<tree[now].v) maxx=tree[now].v,nows=now; int nex=(v<=tree[now].v)?0:1; now=tree[now].ch[nex]; } if(op==4) ans=max(ans,maxx); return nows; } int super2(int nowroot,int v) { int maxx=-0x7fffffff,nows=0; int now=nowroot; while(now) { if(tree[now].v<=v) if(maxx<tree[now].v) maxx=tree[now].v,nows=now; int nex=(v<tree[now].v)?0:1; now=tree[now].ch[nex]; } return nows; } int upper(int nowroot,int v) { int minn=0x7fffffff,nows=0; int now=nowroot; while(now) { if(tree[now].v>v) if(minn>tree[now].v) minn=tree[now].v,nows=now; int nex=(v<tree[now].v)?0:1; now=tree[now].ch[nex]; } ans=min(ans,minn); return nows; } void ask(int k,int l,int r,int ql,int qr,int v) { if(ql<=l&&r<=qr) { int now=super(root[k],v); if(now) Splay(now,0,k); if(now) ans+=tree[now].sum-tree[tree[now].ch[1]].sum; return ; } int mid=(l+r)>>1; if(ql<=mid) ask(k<<1,l,mid,ql,qr,v); if(mid<qr) ask(k<<1|1,mid+1,r,ql,qr,v); } void ask2(int k,int l,int r,int ql,int qr,int v) { if(ql<=l&&r<=qr) { int now=super2(root[k],v); if(now) Splay(now,0,k); if(now) sum+=tree[now].sum-tree[tree[now].ch[1]].sum; return ; } int mid=(l+r)>>1; if(ql<=mid) ask2(k<<1,l,mid,ql,qr,v); if(mid<qr) ask2(k<<1|1,mid+1,r,ql,qr,v); } void change(int k,int l,int r,int x,int v1,int v2) { pop(root[k],v1,k); push(root[k],v2,k); if(l==r) return ; int mid=(l+r)>>1; if(x<=mid) change(k<<1,l,mid,x,v1,v2); else change(k<<1|1,mid+1,r,x,v1,v2); } void ask3(int k,int l,int r,int ql,int qr,int v) { if(ql<=l&&r<=qr) { int now=super(root[k],v); if(now) Splay(now,0,k); return ; } int mid=(l+r)>>1; if(ql<=mid) ask3(k<<1,l,mid,ql,qr,v); if(mid<qr) ask3(k<<1|1,mid+1,r,ql,qr,v); } void ask4(int k,int l,int r,int ql,int qr,int v) { if(ql<=l&&r<=qr) { int now=upper(root[k],v); if(now) Splay(now,0,k); return ; } int mid=(l+r)>>1; if(ql<=mid) ask4(k<<1,l,mid,ql,qr,v); if(mid<qr) ask4(k<<1|1,mid+1,r,ql,qr,v); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); for(int i=1;i<=m;i++) { int x,y,z; scanf("%d",&op); if(op==1) { scanf("%d%d%d",&x,&y,&z); ans=0; ask(1,1,n,x,y,z); printf("%d\n",ans+1); } else if(op==2) { scanf("%d%d%d",&x,&y,&z); ans=-1e8; int l=-1e8,r=1e8; while(l<=r) { int mid=(l+r)>>1; sum=0; ask2(1,1,n,x,y,mid); if(sum>=z) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); } else if(op==3) { scanf("%d%d",&x,&y); change(1,1,n,x,a[x],y); a[x]=y; } else if(op==4) { scanf("%d%d%d",&x,&y,&z); ans=-0x7fffffff; ask3(1,1,n,x,y,z); printf("%d\n",ans); } else { scanf("%d%d%d",&x,&y,&z); ans=0x7fffffff; ask4(1,1,n,x,y,z); printf("%d\n",ans); } } }