[BZOJ3196] 二逼平衡樹 [權值線段樹套位置平衡樹]
阿新 • • 發佈:2018-11-09
題面
思路
沒錯我就是要不走尋常路!
看看那些外層位置資料結構,必須二分的,$O(n\log^3 n)$的做法吧!
看看那些cdq分治/樹狀陣列套線段樹的,空間$O(n\log^2 n)$擠擠擠開不下的做法吧!
這些都不是最優秀的,我來寫一種理論複雜度為時間$O(n\log n\log (m+n))$,空間$O(n\log (n+m))$的做法
我們首先考慮時間問題:為什麼傳統做法的複雜度是3個$log$的?
核心問題是他們有一個二分,否則無法處理第二種詢問
那麼可以看到第二種詢問本質上就是關於區間中權值從小到大排序以後提取出某一位作為答案
考慮把內外層資料結構的意義交換:外層維護權值,內層維護位置
對於詢問一:
這裡就是簡單查詢,在外層數對應的區間範圍內查詢一共有多少個內層數上位於詢問區間內的點即可
實現函式為$getk$
對於詢問二:
首先,答案就是這個區間內排名為第$k$的數的權值
我們在外層樹上二分查詢,每次詢問內層中外層當前節點的左兒子詢問區間中節點個數
如果左邊比需要的大就遞迴進入左邊,否則需要的值先減去左邊的總數,再進入右邊
最後返回葉節點的權值
程式碼實現為$query$函式
對於修改三
刪除再插入即可
對於詢問四
我們首先查詢,在給定區間內,比詢問的數小的數有多少個(用$getk$函式)
然後,其實這個數量,等價於排名
所以我們把排名+1,再用$query$函式輸出這個排名對應的數
對於詢問五,基本與詢問4等價,不贅述
綜上,這裡的核心思路其實就是:
比某個數小的數字的個數就是這個數的排名
實現的時候,注意外層和內層不同於常規樹套樹
使用指標or引用可以得到比較好的效果
同時,如果不離散化,時間複雜度和空間複雜度裡面的那個$\log(n+m)$都會變成$\log 10^8$
不過也是可以過的
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cassert> #include<queue> using namespace std; inline int read(){ int re=0,flag=1;char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') flag=-1; ch=getchar(); } while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar(); return re*flag; } namespace spl{ int ch[6000010][2],w[6000010],siz[6000010],fa[6000010],cnt; void update(int cur){ siz[cur]=siz[ch[cur][0]]+siz[ch[cur][1]]+1; } int newnode(int f,int val){ cnt++; fa[cnt]=f;siz[cnt]=1;ch[cnt][0]=ch[cnt][1]=0;w[cnt]=val; return cnt; } bool get(int cur){return ch[fa[cur]][1]==cur;} void rotate(int x){ int f=fa[x],ff=fa[f],son=get(x); ch[f][son]=ch[x][son^1]; if(ch[f][son]) fa[ch[f][son]]=f; fa[f]=x;ch[x][son^1]=f; fa[x]=ff; if(ff) ch[ff][ch[ff][1]==f]=x; update(f);update(x); } void splay(int x,int to,int &root){ for(int f;(f=fa[x])!=to;rotate(x)){ if(fa[f]!=to) rotate((get(x)==get(f))?f:x); } if(!to) root=x; } void insert(int &cur,int val,int f,int &root){ if(!cur){ cur=newnode(f,val); splay(cur,0,root); return; } siz[cur]++; if(w[cur]>=val) insert(ch[cur][0],val,cur,root); else insert(ch[cur][1],val,cur,root); } int getpos(int cur,int val){ if(w[cur]==val) return cur; if(w[cur]>val) return getpos(ch[cur][0],val); else return getpos(ch[cur][1],val); } int pre(int root){ int u=ch[root][0]; while(ch[u][1]) u=ch[u][1]; return u; } void del(int val,int &root){ int cur=getpos(root,val);splay(cur,0,root); if(!ch[cur][0]&&!ch[cur][1]){root=0;return;} if(!ch[cur][0]&&ch[cur][1]){ fa[ch[cur][1]]=0;root=ch[cur][1]; siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0; return; } if(ch[cur][0]&&!ch[cur][1]){ fa[ch[cur][0]]=0;root=ch[cur][0]; siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0; return; } int p=pre(cur),suf=ch[cur][1]; splay(p,0,root); assert(fa[cur]==p); assert(ch[cur][0]==0); ch[p][1]=suf;fa[suf]=p;update(p); siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0; } int pre(int val,int &root){ int u=root,re=0,minn=1e9; while(u){ if(w[u]>=val) u=ch[u][0]; else{ if(val-w[u]<minn){minn=val-w[u];re=u;} u=ch[u][1]; } } splay(re,0,root); return re; } int suf(int val,int &root){ int u=root,re=0,minn=1e9; while(u){ if(w[u]<=val) u=ch[u][1]; else{ if(w[u]-val<minn){minn=w[u]-val;re=u;} u=ch[u][0]; } } splay(re,0,root); return re; } int query(int &root,int l,int r){ int x=pre(l,root),y=suf(r,root); splay(x,0,root);splay(y,x,root); return siz[ch[y][0]]; } } namespace ct{ int cnt,re;queue<int>q; int newnode(){ if(!q.empty()){re=q.front();q.pop();return re;} else return ++cnt; } void del(int num){ q.push(num); } } namespace seg{ int root[1000010],ch[1000010][2]; void insert(int &cur,int l,int r,int pos,int val){//pos is value in original list, val is position if(!cur){ cur=ct::newnode(); spl::insert(root[cur],-1e6,0,root[cur]); spl::insert(root[cur],1e6,0,root[cur]); } spl::insert(root[cur],val,0,root[cur]); if(l==r) return; int mid=(l+r)>>1; if(mid>=pos) insert(ch[cur][0],l,mid,pos,val); else insert(ch[cur][1],mid+1,r,pos,val); } void del(int &cur,int l,int r,int pos,int val){ spl::del(val,root[cur]); if(l^r){ int mid=(l+r)>>1; if(mid>=pos) del(ch[cur][0],l,mid,pos,val); else del(ch[cur][1],mid+1,r,pos,val); } if(spl::siz[root[cur]]==2){ ct::del(cur); spl::del(-1e6,root[cur]); spl::del(1e6,root[cur]); ch[cur][0]=ch[cur][1]=root[cur]=0; cur=0; } } int getk(int &cur,int l,int r,int qx,int qy,int ql,int qr){ if(!cur) return 0; if(l>=ql&&r<=qr) return spl::query(root[cur],qx,qy); int mid=(l+r)>>1,re=0; if(mid>=ql) re+=getk(ch[cur][0],l,mid,qx,qy,ql,qr); if(mid<qr) re+=getk(ch[cur][1],mid+1,r,qx,qy,ql,qr); return re; } int query(int cur,int l,int r,int pos,int qx,int qy){ if(l==r) return l; int tmp=0; if(ch[cur][0]) tmp=spl::query(root[ch[cur][0]],qx,qy); int mid=(l+r)>>1; if(tmp>=pos) return query(ch[cur][0],l,mid,pos,qx,qy); else return query(ch[cur][1],mid+1,r,pos-tmp,qx,qy); } } int a[50010],n,m,rt=0; int main(){ using namespace seg; n=read();m=read(); int i,t1,t2,t3,t4,tmp; for(i=1;i<=n;i++){ a[i]=read(); insert(rt,0,1e8,a[i],i); } while(m--){ t1=read(); if(t1==1){ t2=read();t3=read();t4=read(); if(t4==0) puts("1"); else printf("%d\n",getk(rt,0,1e8,t2,t3,0,t4-1)+1); } if(t1==2){ t2=read();t3=read();t4=read(); printf("%d\n",query(rt,0,1e8,t4,t2,t3)); } if(t1==3){ t2=read();t3=read(); del(rt,0,1e8,a[t2],t2); insert(rt,0,1e8,a[t2]=t3,t2); } if(t1==4){ t2=read();t3=read();t4=read(); tmp=getk(rt,0,1e8,t2,t3,0,t4-1); if(tmp==0) puts("-2147483647"); else printf("%d\n",query(rt,0,1e8,tmp,t2,t3)); } if(t1==5){ t2=read();t3=read();t4=read(); tmp=getk(rt,0,1e8,t2,t3,0,t4); if(tmp==t3-t2+1) puts("2147483647"); else printf("%d\n",query(rt,0,1e8,tmp+1,t2,t3)); } } }