洛谷 P3380 【模板】二逼平衡树(树套树)-线段树套splay
P3380 【模板】二逼平衡树(树套树)
题目描述
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
-
查询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各输出一行,表示查询结果
输入输出样例
输入样例#1: 复制
9 6 4 2 2 1 9 4 0 1 1 2 1 4 3 3 4 10 2 1 4 3 1 2 5 9 4 3 9 5 5 2 8 5
输出样例#1: 复制
2 4 3 4 9
说明
时空限制:2s,128M
n,m \leq 5\cdot {10}^4n,m≤5⋅104 保证有序序列所有值在任何时刻满足 [0, {10} ^8][0,108]
题目来源:bzoj3196 / Tyvj1730 二逼平衡树,在此鸣谢
此数据为洛谷原创。(特别提醒:此数据不保证操作4、5一定存在,故请务必考虑不存在的情况)
思路:把普通平衡树代码中rtrt改成rt[pos],写一个线段树,线段树每个点都挂一个splay。
数据范围开22倍正好能过,写的时候老是RE和MLE,最后就是试数据范围过了。。。
代码(自己的板子没写过,百度的其他人的板子):
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e4*22,inf=2147483647; 4 #define lc ch[0] 5 #define rc ch[1] 6 int sz[N],fa[N],cnt[N],a[N],ans,mid,l,r,ll,rr,k,rt[N],tot,val[N],ch[2][N],n,m,i,opt; 7 inline char gc(){ 8 static char buf[100000],*p1=buf,*p2=buf; 9 return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; 10 } 11 inline int rd(){ 12 int x=0,fl=1;char ch=gc(); 13 for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1; 14 for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48); 15 return x*fl; 16 } 17 inline void wri(int a){if(a<0)a=-a,putchar('-');if(a>=10)wri(a/10);putchar(a%10|48);} 18 inline void wln(int a){wri(a);puts("");} 19 //---------------------------------------------------------平衡树 20 void upd(int x){sz[x]=sz[lc[x]]+sz[rc[x]]+cnt[x];} 21 void rot(int x){ 22 int y=fa[x],z=fa[y];bool f=x==rc[y]; 23 if (z) ch[y==rc[z]][z]=x; 24 fa[x]=z,fa[y]=x,fa[ch[!f][x]]=y,ch[f][y]=ch[!f][x],ch[!f][x]=y; 25 upd(y); 26 } 27 void splay(int pos,int x){ 28 while (fa[x]){ 29 int y=fa[x],z=fa[y]; 30 if (z) rot((y==rc[z])^(x==rc[y])?x:y); 31 rot(x); 32 } 33 upd(x); 34 rt[pos]=x; 35 } 36 void ins(int pos,int x){ 37 int t=rt[pos],ff=0; 38 while (t){ 39 sz[t]++; 40 if (x==val[t]){ 41 cnt[t]++; 42 return; 43 } 44 ff=t,t=ch[x>val[t]][t]; 45 } 46 t=++tot; 47 ch[x>val[ff]][ff]=t; 48 fa[t]=ff,val[t]=x,lc[t]=rc[t]=0,sz[t]=cnt[t]=1; 49 splay(pos,t); 50 } 51 int find(int pos,int x){ 52 int t=rt[pos]; 53 do{ 54 if (x==val[t]) break; 55 t=ch[x>val[t]][t]; 56 }while (1); 57 splay(pos,t); 58 return t; 59 } 60 int Rank(int pos,int x){ 61 int t=rt[pos],ret=0; 62 while (t){ 63 if (x==val[t]) return ret+sz[lc[t]]; 64 if (x>val[t]){ 65 ret+=sz[lc[t]]+cnt[t]; 66 t=rc[t]; 67 }else t=lc[t]; 68 } 69 return ret; 70 } 71 //int Max(int x){return rc[x]?Max(rc[x]):x;} 72 int Max(int t){while (rc[t]) t=rc[t];return t;} 73 void del(int pos,int x){ 74 x=find(pos,x); 75 int ls=lc[x],rs=rc[x]; 76 if (--cnt[x]) return; 77 if (!ls && !rs){//不能直接return,先清空一下 78 int &k=rt[pos]; 79 k=fa[k]=lc[k]=rc[k]=sz[k]=cnt[k]=val[k]=0; 80 return; 81 } 82 if (!ls) rt[pos]=rc[x],fa[rt[pos]]=0; 83 else if (!rs) rt[pos]=lc[x],fa[rt[pos]]=0; 84 else{ 85 int lson=Max(ls);swap(ls,lson); 86 fa[lson]=0,splay(pos,ls),rc[ls]=rs,fa[rs]=ls,upd(ls); 87 } 88 } 89 /*int PRE(int t,int x){ 90 if (!t) return 0; 91 if (x<=val[t]) return PRE(lc[t],x); 92 int tmp=PRE(rc[t],x); 93 return tmp?tmp:t; 94 } 95 int pre(int pos,int x){return val[PRE(rt[pos],x)];}*/ 96 int pre(int pos,int x){ 97 int t=rt[pos];int ans=-inf; 98 while (t) 99 if (x>val[t]) ans=max(ans,val[t]),t=rc[t]; 100 else t=lc[t]; 101 return ans; 102 } 103 int nxt(int pos,int x){ 104 int t=rt[pos];int ans=inf; 105 while (t) 106 if (x<val[t]) ans=min(ans,val[t]),t=lc[t]; 107 else t=rc[t]; 108 return ans; 109 } 110 //---------------------------------------------------------线段树 111 #define mid ((l+r)>>1)//如果没加,这个mid就变二分里的mid了 112 void insert(int t,int l,int r,int x,int v){ 113 ins(t,v);//和下面的修改一样,我刚开始是在l==r的时候插入的 114 if (l==r) return; 115 if (x<=mid) insert(t<<1,l,mid,x,v); 116 else insert(t<<1|1,mid+1,r,x,v); 117 //函数开始的ins()作用跟update()差不多 118 } 119 void change(int t,int l,int r,int x,int v){ 120 del(t,a[x]),ins(t,v);//刚开始写的是del(t,v) 121 if (l==r) return; 122 if (x<=mid) change(t<<1,l,mid,x,v); 123 else change(t<<1|1,mid+1,r,x,v); 124 } 125 int query(int t,int l,int r,int x,int y,int v){ 126 if (x<=l && r<=y) return Rank(t,v); 127 // if (x<=l && r<=y) return sz[lc[find(t,v)]];----会有不存在v的情况,find()会死循环 128 int ans=0; 129 if (x<=mid) ans+=query(t<<1,l,mid,x,y,v); 130 if (mid<y) ans+=query(t<<1|1,mid+1,r,x,y,v); 131 return ans; 132 } 133 int Prev(int t,int l,int r,int x,int y,int v){ 134 if (x<=l && r<=y) return pre(t,v); 135 int ans=-inf; 136 if (x<=mid) ans=max(ans,Prev(t<<1,l,mid,x,y,v)); 137 if (mid<y) ans=max(ans,Prev(t<<1|1,mid+1,r,x,y,v)); 138 return ans; 139 } 140 int Succ(int t,int l,int r,int x,int y,int v){ 141 if (x<=l && r<=y) return nxt(t,v); 142 int ans=inf; 143 if (x<=mid) ans=min(ans,Succ(t<<1,l,mid,x,y,v)); 144 if (mid<y) ans=min(ans,Succ(t<<1|1,mid+1,r,x,y,v)); 145 return ans; 146 } 147 #undef mid 148 int main(){ 149 n=rd(),m=rd(); 150 for (i=1;i<=n;i++) a[i]=rd(),insert(1,1,n,i,a[i]); 151 for (;m--;){ 152 opt=rd(),l=rd(); 153 if (opt!=3) r=rd(); 154 k=rd(); 155 if (opt==1) wln(query(1,1,n,l,r,k)+1); 156 if (opt==2){ 157 ll=0,rr=1e8; 158 while (ll<=rr){ 159 mid=ll+rr>>1; 160 if (query(1,1,n,l,r,mid)+1<=k) ans=mid,ll=mid+1; 161 else rr=mid-1; 162 /*if (query(1,1,n,l,r,mid)+1<k) ll=mid+1;------比如查询k=1时query()+1一定>=k,会导致不断向左,输出0 163 else ans=mid,rr=mid-1; 164 其实两者本质差别是query()+1==k的时候应该向左还是向右, 165 如果是小于号的话,rr不断向左,query()也变小,导致答案和标程差很多*/ 166 } 167 wln(ans); 168 } 169 if (opt==3) change(1,1,n,l,k),a[l]=k;//a[l]=k不能忘 170 if (opt==4) wln(Prev(1,1,n,l,r,k)); 171 if (opt==5) wln(Succ(1,1,n,l,r,k)); 172 } 173 }