题目链接:https://www.luogu.org/problemnew/show/P3380
感谢:https://blog.csdn.net/chenxiaoran666/article/details/82949397
一个磕了几天的模板题,一个rand()的打错就T到疯。MMP。
自己写的一个板子。
#include<algorithm>
#include<string.h>
#include<stdio.h>
using namespace std;
const int Max=1e4*5+10;
struct node
{
int child[2],s,v,key;
} A[30*Max];
int tot=0,a[Max],see=233;
int n,m;
inline int read()//快读
{
char ch = getchar();
int x = 0, f = 1;
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}
while('0' <= ch && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
class treap//无旋streap树
{
private:
#define rand() ((int)(see*=482711ll % 2147483647))//我TM之前忘记打了=号,T了一天,找了一天的bug
#define ls(x) A[x].child[0]
#define rs(x) A[x].child[1]
void up(int r)
{
A[r].s=A[ls(r)].s+A[rs(r)].s+1;
return ;
}
public:
int root=0;
void Split(int r,int &x,int &y,int k)
{
if(!r)
{
x=y=0;
return ;
}
if(A[r].v<=k) x=r,Split(rs(r),rs(x),y,k);
else y=r,Split(ls(r),x,ls(y),k);
up(r);
return ;
}
void Merge(int &r,int x,int y)
{
if(!x||!y)
{
r=x+y;
return ;
}
if(A[x].key<A[y].key) r=x,Merge(rs(r),rs(x),y);
else r=y,Merge(ls(r),x,ls(y));
up(r);
return ;
}
int Insert(int& r,int k)
{
int x=0,y=0,z=++tot;
ls(z)=rs(z)=0,A[z].v=k,A[z].s=1,A[z].key=rand();
Split(r,x,y,k);
Merge(x,x,z);
Merge(r,x,y);
return 0;
}
int del(int &r,int k)
{
int x=0,y=0,z=0;
Split(r,x,y,k);
Split(x,x,z,k-1);
Merge(z,ls(z),rs(z));
Merge(x,x,z);
Merge(r,x,y);
return 0;
}
int Rank(int r,int k)//这里需要改变一下,ans不用+1。
{
int ans=0;
while(r)
{
if(A[r].v>=k) r=ls(r);
else ans+=A[ls(r)].s+1,r=rs(r);
}
return ans;
}
int kth(int r,int k)
{
if(A[r].s<k) return -1;
while(A[ls(r)].s+1!=k)
{
if(A[ls(r)].s+1>k)r=ls(r);
else k-=A[ls(r)].s+1,r=rs(r);
}
return A[r].v;
}
int pre(int &r,int k)
{
int x=0,y=0,ans;
Split(r,x,y,k-1);
ans=x?kth(x,A[x].s):-2147483647;
Merge(r,x,y);
return ans;
}
int sub(int &r,int k)
{
int x=0,y=0,ans;
Split(r,x,y,k);
ans=y?kth(y,1):2147483647;
Merge(r,x,y);
return ans;
}
};
class segment//线段树
{
private:
#define rt T[k].root
treap T[4*Max];
public:
void Build(int l,int r,int k)//建树,每个区间都是一颗平衡树
{
for(int i=l; i<=r; i++) T[k].Insert(rt,a[i]);
int mid=(l+r)>>1;
if(r>l) Build(l,mid,2*k),Build(mid+1,r,2*k+1);
return ;
}
void update(int l,int r,int k,int pos,int x)//每个区间都更新,先把值删去,在加入新的值
{
if(l<=pos&&pos<=r) T[k].del(rt,a[pos]),T[k].Insert(rt,x);
int mid=(l+r)>>1;
if(r>l) pos>mid?update(mid+1,r,2*k+1,pos,x):update(l,mid,2*k,pos,x);
return ;
}
int _rank(int l,int r,int k,int ql,int qr,int x)//找出每个区间小于x的数的和
{
int ans=0;
int mid=(l+r)>>1;
if(ql<=l&&r<=qr) return T[k].Rank(rt,x);
if(ql<=mid) ans+=_rank(l,mid,2*k,ql,qr,x);
if(qr>mid) ans+=_rank(mid+1,r,2*k+1,ql,qr,x);
return ans;
}
int _kth(int ql,int qr,int k)//二分,看看那个值符合第k大,“最大”的符合值就是答案
{
int l=0,r=1e8;
while(l<=r)
{
int mid=(l+r)>>1;
if(_rank(1,n,1,ql,qr,mid)+1<=k) l=mid+1;
else r=mid-1;
}
return r;
}
int _pre(int l,int r,int k,int ql,int qr,int x)//求所有离散区间前驱,取最大值
{
int ans=-2147483647;
int mid=(l+r)>>1;
if(ql<=l&&r<=qr)
{
return T[k].pre(rt,x);
}
if(ql<=mid) ans=max(ans,_pre(l,mid,2*k,ql,qr,x));
if(qr>mid) ans=max(ans,_pre(mid+1,r,2*k+1,ql,qr,x));
return ans;
}
int _sub(int l,int r,int k,int ql,int qr,int x)//求所有离散区间的后继,取最小值
{
int ans=2147483647;
int mid=(l+r)>>1;
if(ql<=l&&r<=qr) return T[k].sub(rt,x);
if(ql<=mid) ans=min(ans,_sub(l,mid,2*k,ql,qr,x));
if(qr>mid) ans=min(ans,_sub(mid+1,r,2*k+1,ql,qr,x));
return ans;
}
};
int main()
{
// freopen("1.txt","r",stdin);
// freopen("3.txt","w",stdout);
int opt,x,y,z;
n=read(),m=read();
for(int i=1; i<=n; i++)
a[i]=read();
segment mary;
mary.Build(1,n,1);
while(m--)
{
opt=read();
if(opt==1)//查询k在区间内的排名
{
x=read(),y=read(),z=read();
printf("%d\n",mary._rank(1,n,1,x,y,z)+1);
}
if(opt==2)//查询区间内排名为k的值
{
x=read(),y=read(),z=read();
printf("%d\n",mary._kth(x,y,z));
}
if(opt==3)//修改某一位值上的数值
{
x=read(),z=read();
mary.update(1,n,1,x,z);
a[x]=z;
}
if(opt==4)//查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
{
x=read(),y=read(),z=read();
printf("%d\n",mary._pre(1,n,1,x,y,z));
}
if(opt==5)//查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)
{
x=read(),y=read(),z=read();
printf("%d\n",mary._sub(1,n,1,x,y,z));
}
}
return 0;
}
/*
输入格式:
第一行两个数 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各输出一行,表示查询结果
*/