Time Limit: 10 Sec
Memory Limit: 128 MB
Description
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
Input
第一行两个数 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的后继
Output
对于操作1,2,4,5各输出一行,表示查询结果
HINT
1.n和m的数据范围:n,m<=50000
2.序列中每个数的数据范围:[0,1e8]
3.虽然原题没有,但事实上5操作的k可能为负数
洛谷题面4,5操作稍有不同,但以下程序依然适用
题目分析:
树套树的方式多种多样
这里先讲一讲线段树套Treap
思路非常的暴力
线段树每个结点都是一棵treap
用来维护该区间内的数
1.rank操作,在线段树找到对应区间,将每个区间的排名加起来
2.kth操作,直接二分数值,将二分数值在给定区间内rank,看是否等于k
3.modify操作,直接暴力将原数从每个treap中删除在插入新数
4.5.pre.nxt操作,找到线段树对应区间查询
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int inf=2147483647;
const int maxn=2000010;
int n,m;
int a[maxn],val[maxn],r[maxn];
int sum[maxn],cnt[maxn],ch[maxn][2],tot;
int rt[maxn];//rt[]表示线段树每个节点保存的treap的树根
void update(int p){ sum[p]=cnt[p]+sum[ch[p][0]]+sum[ch[p][1]];}
void rotate(int &p,int d)
{
int k=ch[p][d^1];
ch[p][d^1]=ch[k][d];
ch[k][d]=p;
update(p); update(k);
p=k;
}
void ins(int &p,int x)//和普通treap的插入一样
{
if(!p)
{
p=++tot; val[p]=x; r[p]=rand();
sum[p]=cnt[p]=1;
return;
}
if(val[p]==x){ ++sum[p]; ++cnt[p]; return;}
int d=x<val[p] ?0:1;
ins(ch[p][d],x);
if(r[ch[p][d]]<r[p]) rotate(p,d^1);
update(p);
}
void build(int s,int t,int p)
{
for(int i=s;i<=t;++i) ins(rt[p],a[i]);//建立该区间的treap
if(s==t) return;
int mid=s+t>>1;
build(s,mid,p<<1); build(mid+1,t,p<<1|1);
}
int rank_treap(int p,int x)//和普通treap的rank一样
{
if(!p) return 0;
if(val[p]==x) return sum[ch[p][0]];
if(x<val[p]) return rank_treap(ch[p][0],x);
else return sum[ch[p][0]]+cnt[p]+rank_treap(ch[p][1],x);
}
int rank_seg(int ll,int rr,int s,int t,int p,int v)//线段树找对应区间,排名相加
{
if(ll<=s&&t<=rr) return rank_treap(rt[p],v);
int mid=s+t>>1,ans=0;
if(ll<=mid) ans+=rank_seg(ll,rr,s,mid,p<<1,v);
if(rr>mid) ans+=rank_seg(ll,rr,mid+1,t,p<<1|1,v);
return ans;
}
int kth(int ll,int rr,int k)//暴力二分再rank
{
int L=0,R=1e8,mid;
while(L<R)
{
mid=L+R+1>>1;
if(rank_seg(ll,rr,1,n,1,mid)>=k) R=mid-1;
else L=mid;
}
return L;
}
void del(int &p,int x)//普通treap的del
{
if(!p) return;
if(val[p]==x)
{
if(cnt[p]>1){ --sum[p]; --cnt[p]; return;}
else
{
if(!ch[p][0]) p=ch[p][1];
else if(!ch[p][1]) p=ch[p][0];
else
{
int dd=r[ch[p][0]]<r[ch[p][1]] ?1:0;
rotate(p,dd); del(ch[p][dd],x);
}
}
}
else if(x<val[p]) del(ch[p][0],x);
else del(ch[p][1],x);
if(p) update(p);
}
void modify(int s,int t,int p,int pos,int val)
{
del(rt[p],a[pos]); ins(rt[p],val);//暴力删除再重新插入
if(s==t) return;
int mid=s+t>>1;
if(pos<=mid) modify(s,mid,p<<1,pos,val);//相当于线段树的单点修改
else modify(mid+1,t,p<<1|1,pos,val);
}
int pre_treap(int p,int x)
{
if(!p)return -inf;//没有前驱返回-inf
if(x<=val[p]) return pre_treap(ch[p][0],x);//找到一个,但不能确定是最大的
return max(val[p],pre_treap(ch[p][1],x));
}
int nxt_treap(int p,int x)//同pre
{
if(!p)return inf;
if(x>=val[p]) return nxt_treap(ch[p][1],x);
return min(val[p],nxt_treap(ch[p][0],x));
}
int pre_seg(int ll,int rr,int s,int t,int p,int v)
{
if(ll<=s&&t<=rr) return pre_treap(rt[p],v);
int mid=s+t>>1,ans=-inf;
if(ll<=mid) ans=max(ans,pre_seg(ll,rr,s,mid,p<<1,v));
if(rr>mid) ans=max(ans,pre_seg(ll,rr,mid+1,t,p<<1|1,v));
return ans;
}
int nxt_seg(int ll,int rr,int s,int t,int p,int v)
{
if(ll<=s&&t<=rr) return nxt_treap(rt[p],v);
int mid=s+t>>1,ans=inf;
if(ll<=mid) ans=min(ans,nxt_seg(ll,rr,s,mid,p<<1,v));
if(rr>mid) ans=min(ans,nxt_seg(ll,rr,mid+1,t,p<<1|1,v));
return ans;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)a[i]=read();
build(1,n,1);//线段树套treap建树
while(m--)
{
int k=read(),ll=read(),rr=read();
if(k==1) printf("%d\n",rank_seg(ll,rr,1,n,1,read())+1);
else if(k==2) printf("%d\n",kth(ll,rr,read()));
else if(k==3) modify(1,n,1,ll,rr),a[ll]=rr;
else if(k==4) printf("%d\n",pre_seg(ll,rr,1,n,1,read()));
else if(k==5) printf("%d\n",nxt_seg(ll,rr,1,n,1,read()));
}
return 0;
}