洛谷 P3380 【模板】二逼平衡树(树套树)-线段树套splay

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

题目描述

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

  1. 查询k在区间内的排名

  2. 查询区间内排名为k的值

  3. 修改某一位值上的数值

  4. 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)

  5. 查询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,m5104 保证有序序列所有值在任何时刻满足 [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 }

猜你喜欢

转载自www.cnblogs.com/ZERO-/p/10883533.html
今日推荐