Problem
写一种数据结构,来维护一个有序数列,其中需要提供以下操作:
- 查询 在区间内的排名。
- 查询区间内排名为 的值。
- 修改某一位值上的数值。
- 查询 在区间内的前驱 前驱定义为严格小于 ,且最大的数,若不存在输出 。
- 查询 在区间内的后继 后继定义为严格大于 ,且最小的数,若不存在输出 。
Solution
这就是树套树的模板题啦。
我用的就是线段树套非旋Treap。
具体做法就是对于线段树的每一个区间节点,都按照这个区间建非旋Treap。
然后询问的时候就按照区间来查询就可以了。
时间复杂度 ,注意空间开大一点。
具体的细节之类的就看代码吧。
Code
#include<ctime>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5000005
#define inf ((1ll<<31ll)-1)
using namespace std;
int n,m,a[N];
namespace FHQ_Treap
{
int tot=0,val[N],Size[N],key[N],lc[N],rc[N];
int newnode(int k)
{
val[++tot]=k,key[tot]=rand();
Size[tot]=1,lc[tot]=rc[tot]=0;
return tot;
}
void split(int root,int &r1,int &r2,int k)
{
if(!root) {r1=r2=0;return;}
if(val[root]<=k) r1=root,split(rc[root],rc[r1],r2,k);
else r2=root,split(lc[root],r1,lc[r2],k);
Size[root]=Size[lc[root]]+Size[rc[root]]+1;
}
void Merge(int &root,int r1,int r2)
{
if(!r1||!r2) {root=r1+r2;return;}
if(key[r1]<key[r2]) root=r1,Merge(rc[root],rc[r1],r2);
else root=r2,Merge(lc[root],r1,lc[r2]);
Size[root]=Size[lc[root]]+Size[rc[root]]+1;
}
void Insert(int &root,int k)
{
int r1=0,r2=0;
int x=newnode(k);
split(root,r1,r2,k);
Merge(r1,r1,x);
Merge(root,r1,r2);
}
void Delete(int &root,int k)
{
int r1=0,r2=0,r3=0;
split(root,r1,r2,k);
split(r1,r1,r3,k-1);
Merge(r3,lc[r3],rc[r3]);
Merge(r1,r1,r3);
Merge(root,r1,r2);
}
int Rank(int root,int k)
{
int r1=0,r2=0;
split(root,r1,r2,k-1);
int ans=Size[r1];
Merge(root,r1,r2);
return ans;
}
int kth(int root,int k)
{
if(!root) return 0;
if(Size[lc[root]]+1==k) return root;
if(Size[lc[root]]+1>k) return kth(lc[root],k);
return kth(rc[root],k-Size[lc[root]]-1);
}
int Prefix(int root,int k)
{
int r1=0,r2=0;
split(root,r1,r2,k-1);
if(!r1) return -inf;
int x=kth(r1,Size[r1]);
Merge(root,r1,r2);
return val[x];
}
int Suffix(int root,int k)
{
int r1=0,r2=0;
split(root,r1,r2,k);
if(!r2) return inf;
int x=kth(r2,1);
Merge(root,r1,r2);
return val[x];
}
}
using namespace FHQ_Treap;
namespace Segment_Tree
{
int Root[N];
#define mid ((l+r)>>1)
void build(int rt,int l,int r)
{
for(int i=l;i<=r;++i)
Insert(Root[rt],a[i]);
if(l==r) return;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
}
void Modify(int rt,int l,int r,int pos,int k)
{
Delete(Root[rt],a[pos]);
Insert(Root[rt],k);
if(l==r) return;
if(pos<=mid) Modify(rt<<1,l,mid,pos,k);
else Modify(rt<<1|1,mid+1,r,pos,k);
}
int QueryRank(int rt,int l,int r,int x,int y,int k)
{
if(l>=x&&r<=y) return Rank(Root[rt],k);
int ans=0;
if(x<=mid) ans+=QueryRank(rt<<1,l,mid,x,y,k);
if(y>mid) ans+=QueryRank(rt<<1|1,mid+1,r,x,y,k);
return ans;
}
int QueryPrefix(int rt,int l,int r,int x,int y,int k)
{
if(l>=x&&r<=y) return Prefix(Root[rt],k);
int ans=-inf;
if(x<=mid) ans=max(ans,QueryPrefix(rt<<1,l,mid,x,y,k));
if(y>mid) ans=max(ans,QueryPrefix(rt<<1|1,mid+1,r,x,y,k));
return ans;
}
int QuerySuffix(int rt,int l,int r,int x,int y,int k)
{
if(l>=x&&r<=y) return Suffix(Root[rt],k);
int ans=inf;
if(x<=mid) ans=min(ans,QuerySuffix(rt<<1,l,mid,x,y,k));
if(y>mid) ans=min(ans,QuerySuffix(rt<<1|1,mid+1,r,x,y,k));
return ans;
}
#undef mid
}
using namespace Segment_Tree;
int Querykth(int x,int y,int k)
{
int l=0,r=1e8;
while(l<r)
{
int mid=(l+r)>>1;
if(QueryRank(1,1,n,x,y,mid)<k) l=mid+1;
else r=mid;
}
return l-1;
}
int main()
{
srand(time(0));
int l,r,i,k,op;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&op,&l,&r);
if(op!=3) scanf("%d",&k);
if(op==1) printf("%d\n",QueryRank(1,1,n,l,r,k)+1);
else if(op==2) printf("%d\n",Querykth(l,r,k));
else if(op==3) Modify(1,1,n,l,r),a[l]=r;
else if(op==4) printf("%d\n",QueryPrefix(1,1,n,l,r,k));
else printf("%d\n",QuerySuffix(1,1,n,l,r,k));
}
return 0;
}