洛谷 P3380 bzoj3196 Tyvj1730 【模板】二逼平衡樹(樹套樹)
【模板】二逼平衡樹(樹套樹)
題目描述
您需要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:
查詢k在區間內的排名
查詢區間內排名為k的值
修改某一位值上的數值
查詢k在區間內的前驅(前驅定義為嚴格小於x,且最大的數,若不存在輸出-2147483647)
查詢k在區間內的後繼(後繼定義為嚴格大於x,且最小的數,若不存在輸出2147483647)
註意上面兩條要求和tyvj或者bzoj不一樣,請註意
輸入輸出格式
輸入格式:
第一行兩個數 n,m 表示長度為n的有序序列和m個操作
第二行有n個數,表示有序序列
下面有m行,opt表示操作標號
若opt=1 則為操作1,之後有三個數l,r,k 表示查詢k在區間[l,r]的排名
若opt=2 則為操作2,之後有三個數l,r,k 表示查詢區間[l,r]內排名為k的數
若opt=3 則為操作3,之後有兩個數pos,k 表示將pos位置的數修改為k
若opt=4 則為操作4,之後有三個數l,r,k 表示查詢區間[l,r]內k的前驅
若opt=5 則為操作5,之後有三個數l,r,k 表示查詢區間[l,r]內k的後繼
輸出格式:
對於操作1,2,4,5各輸出一行,表示查詢結果
說明
時空限制:2s,1256M
\(n,m \leq 5\cdot {10}^4\)
保證有序序列所有值在任何時刻滿足\([0, {10} ^8]\)
題目來源:bzoj3196 / Tyvj1730 二逼平衡樹,在此鳴謝
此數據為洛谷原創。(特別提醒:此數據不保證操作4、5一定存在,故請務必考慮不存在的情況)
前幾天寫了個樹狀數組套平衡樹,最後懶得調了
花了很久寫+調弄了樹狀數組套可持久化線段樹
發現有思想的是 在多個可持久化樹上一起二分
很煩人的是查排名時的一些存在性問題
最關鍵的是權值線段樹想離散開點還必須離散
代碼也不好看,以後還會寫的
Code
#include <cstdio> #include <algorithm> #define sf num[i].second.first #define ss num[i].second.second using namespace std; const int N=300010; const int inf=2147483647; int sum[N*50],ls[N*50],rs[N*50],root[N],las[N],tot,n,m,is,n_,n0,dat[N<<1]; pair <int,pair<int,int> > num[N<<1]; struct node { int opt,l,r,k; }op[N]; void change(int &now,int l,int r,int pos,int delta) { if(!now) now=++tot; sum[now]+=delta; if(l==r) return; int mid=l+r>>1; if(pos<=mid) change(ls[now],l,mid,pos,delta); else change(rs[now],mid+1,r,pos,delta); } int query(int now,int l,int r,int pos)//1-pos的值 { if(l==r) return is=sum[now],0; if(!now||!pos) return sum[now]; int mid=l+r>>1; if(pos<=mid) return query(ls[now],l,mid,pos); else return sum[ls[now]]+query(rs[now],mid+1,r,pos); } void add(int x,int pos,int delta) { while(x<=n_) { change(root[x],1,n,pos,delta); x+=x&-x; } } int Rank(int l,int r,int pos)//查詢l-r區間pos的排名 { if(!pos) return 0; int rk=0,cnt0=0; for(int i=l-1;i;i-=i&-i) is=0,rk-=query(root[i],1,n,pos),cnt0-=is; for(int i=r;i;i-=i&-i) is=0,rk+=query(root[i],1,n,pos),cnt0+=is; return rk+(cnt0>0); } int rl[N],rr[N]; int frank(int l,int r,int k)//l到r排名為k的數 { int ad,de; for(int i=l-1;i;i-=i&-i) rl[i]=root[i]; for(int i=r;i;i-=i&-i) rr[i]=root[i]; int L=1,R=n; while(L<R) { ad=de=0; int Mid=L+R>>1; for(int i=l-1;i;i-=i&-i) de+=sum[ls[rl[i]]]; for(int i=r;i;i-=i&-i) ad+=sum[ls[rr[i]]]; if(ad-de>=k) { R=Mid; for(int i=l-1;i;i-=i&-i) rl[i]=ls[rl[i]]; for(int i=r;i;i-=i&-i) rr[i]=ls[rr[i]]; } else { L=Mid+1; k-=ad-de; for(int i=l-1;i;i-=i&-i) rl[i]=rs[rl[i]]; for(int i=r;i;i-=i&-i) rr[i]=rs[rr[i]]; } } return dat[L]; } void pre(int l,int r,int pos) { int rk=Rank(l,r,pos-1); if(!rk) printf("%d\n",-inf); else printf("%d\n",frank(l,r,rk)); } void suc(int l,int r,int pos) { add(l,pos+1,1); int rk=Rank(l,r,pos+1); add(l,pos+1,-1); if(rk==r+2-l) printf("%d\n",inf); else printf("%d\n",frank(l,r,rk)); } void init() { scanf("%d%d",&n_,&m); for(int i=1;i<=n_;i++) { scanf("%d",&num[i].first); num[i].second.first=i; } n0=n_; for(int i=1;i<=m;i++) { scanf("%d",&op[i].opt); if(op[i].opt==3) scanf("%d%d",&op[i].l,&op[i].k); else scanf("%d%d%d",&op[i].l,&op[i].r,&op[i].k); if(op[i].opt!=2) { num[++n0].first=op[i].k; num[n0].second.first=i+n_; } } sort(num+1,num+1+n0); num[0].first=inf; for(int i=1;i<=n0;i++) { if(num[i].first!=num[i-1].first) ++n; ss=n,dat[n]=num[i].first; } for(int i=1;i<=n0;i++) { if(sf<=n_) las[sf]=ss,add(sf,ss,1); else op[sf-n_].k=ss; } } void work() { for(int i=1;i<=m;i++) { if(op[i].opt==1) { add(op[i].l,op[i].k,1); printf("%d\n",Rank(op[i].l,op[i].r,op[i].k)); add(op[i].l,op[i].k,-1); } else if(op[i].opt==2) printf("%d\n",frank(op[i].l,op[i].r,op[i].k)); else if(op[i].opt==3) { add(op[i].l,las[op[i].l],-1); las[op[i].l]=op[i].k; add(op[i].l,las[op[i].l],1); } else if(op[i].opt==4) pre(op[i].l,op[i].r,op[i].k); else suc(op[i].l,op[i].r,op[i].k); } } int main() { init(); work(); return 0; }
2018.8.1
洛谷 P3380 bzoj3196 Tyvj1730 【模板】二逼平衡樹(樹套樹)