题解 P3380 【【模板】二逼平衡树(树套树)】

teafrogsf

2017-12-06 21:34:47

Solution

大家好,我非常喜欢暴力数据结构,于是我就用分块过了这道题。 0. 用一个原数组存储原值,另一个数组使得块内元素排序。O(nlogn) 1. 对于1操作,边角暴力,块内二分。O(nsqrt(nlogn))。 2. 对于3操作,原数组O(1)修改,排序数组修改后重新排序即可。 O(nsqrt(nlogn)) 3. 对于4操作,边角暴力,块内二分。 O(nsqrt(nlogn)) 。 4. 对于5操作,边角暴力,块内二分。 O(nsqrt(nlogn)) 。 ## 对于恶心到毁天灭地的2操作,外面套一个二分答案,边角暴力,块内二分,统计答案。 ### O(nlogssqrt(nlogn)) 。s在我的代码中为1e9,但实际上可以优化成查询区间的maximum-minimum。 总体上O(nlogssqrt(nlogn))。但因为毁天灭地的块内二分我一直写不出,导致12操作迫不得已使用了lower\_bound和upper\_bound,常数上也不小。大牛上最大点500ms,普通站70分。 **P.S.为了维护dalao们的眼睛,我删去了调试部分代码,所以这上面并没有血的历史。为了这道题我奋斗了22天,尝试了各种各样的写法,把自己的代码在ftp、洛谷、mail上传来传去,找到了分块做区间第k大和树套树的两种std,加上几十次对拍,终于搞出了正解。如果大家有幸能够自己尝试用分块写这么一道题,会是一次非常磨砺身心而又具有充实感的挑战。** ```cpp #include<cstdio> #include<cmath> #include<iostream> #include<algorithm> #define neko 50010 #define mid ((L+R)>>1) #define midd ((LL+RR)>>1) #define chkmin(a,b) ((a)<(b)?(a):(b)) #define chkmax(a,b) ((a)>(b)?(a):(b)) #define f(i,a,b) for(register int i=(a);i<=(b);i=-(~(i))) using std::stable_sort; int bl[neko],Le[neko],Re[neko],a[neko],s[neko],n,m,blk; template<typename T> void read(T &x) { char c=getchar();T p=1;x=0; while(!isdigit(c)){if(c=='-')p=-1;c=getchar();} while(isdigit(c)) { x=(x<<1)+(x<<3)+(c^'0'); c=getchar(); }x*=p; } void split() { int pre=1,prei=0;Le[1]=1; f(i,1,n) { s[i]=a[i],bl[i]=(i-1)/blk+1; if(bl[i]>pre) { stable_sort(s+prei,s+i); pre=bl[i],prei=i; Le[bl[i]]=i,Re[bl[i]-1]=i-1; } }Re[bl[n]]=n,stable_sort(s+prei,s+n+1); } int key(int l,int r,int x) { int ans=1,L,R; f(i,l,chkmin(r,Re[bl[l]]))if(a[i]<x)++ans; f(i,bl[l]+1,bl[r]-1)ans+=std::lower_bound(s+Le[i],s+Re[i]+1,x)-(s+Le[i]); if(bl[l]!=bl[r])f(i,chkmax(l,Le[bl[r]]),r)if(a[i]<x)++ans; return ans; } int find(int l,int r,int x) { int num,anss=1,L,R,LL=1,RR=1e9; while(LL<=RR) { num=0; f(i,l,chkmin(r,bl[l]*blk))if(a[i]<=midd)++num; f(i,bl[l]+1,bl[r]-1)num+=std::upper_bound(s+Le[i],s+Re[i]+1,midd)-(s+Le[i]); if(bl[l]!=bl[r]) f(i,chkmax(l,Le[bl[r]]),r) if(a[i]<=midd)++num; if(num>=x)anss=midd,RR=midd-1; else LL=midd+1; }return anss; } void update(int pos,int x) { a[pos]=x;int l=Le[bl[pos]],r=Re[bl[pos]]; f(i,l,r)s[i]=a[i]; stable_sort(s+l,s+r+1); } int lower(int l,int r,int x) { int ans=-2147483647,L,R; f(i,l,chkmin(r,Re[bl[l]]))if(a[i]<x)ans=chkmax(ans,a[i]); f(i,bl[l]+1,bl[r]-1) { L=Le[i],R=Re[i]; while(L<R) { if(s[mid]>=x)R=mid; else L=mid+1; } if(mid==Re[i]) { if(s[mid]<x)ans=chkmax(ans,s[mid]); else ans=chkmax(ans,s[mid-1]); }else if(mid!=Le[i])ans=chkmax(ans,s[mid-1]); } f(i,chkmax(l,Le[bl[r]]),r)if(a[i]<x)ans=chkmax(ans,a[i]); return ans; } int upper(int l,int r,int x) { int ans=2147483647,L,R; f(i,l,chkmin(r,Re[bl[l]]))if(a[i]>x)ans=chkmin(ans,a[i]); f(i,bl[l]+1,bl[r]-1) { L=Le[i],R=Re[i]; while(L<R) { if(s[mid]>x)R=mid; else L=mid+1; } if(mid==Re[i]&&s[mid]<=x)continue; ans=chkmin(ans,s[mid]); } f(i,chkmax(l,Le[bl[r]]),r)if(a[i]>x)ans=chkmin(ans,a[i]); return ans; } int main() { int opt,x,y,k; scanf("%d%d",&n,&m); f(i,1,n)read(a[i]);if(n==1)blk=1;else blk=sqrt(n); split(); f(i,1,m) { read(opt),read(x),read(y); if(opt==1)read(k),printf("%d\n",key(x,y,k)); if(opt==2)read(k),printf("%d\n",find(x,y,k)); if(opt==3)update(x,y); if(opt==4)read(k),printf("%d\n",lower(x,y,k)); if(opt==5)read(k),printf("%d\n",upper(x,y,k)); }return 0; } ```