array
题意
给出一个1,n的排列,有q(q<=1e5)有以下两个操作(强制在线):
1.给位置pos上的数+1e7
2.询问[1,r]区间上大于等于k(1e5)的数最小是多少(这个数不能等于[1,r]区间中的任意一个数)
分析
由数据我们可以知道只要对一个数进行了操作1,相当于把这个数挖掉了,所以我们想要找到最小的满足题意的值,只需要维护区间最大值,只要区间中有值大于等于k,就可以进区间找,但是这样会带来很多问题,首先是区间全是连续的无法插,其次是可以插,如何找到这个空?这个时候需要转化思维,既然普通线段树难以维护,是否考虑主席树or权值线段树,使用权值线段树,维护区间最大的标号,如果标号大于等于r,先往左边找,但是这样可能会导致[1,mid]中有标号比r大的,但是比k小,这样就会一直找到mid,只需要开一个变量记一下,如果[1,mid]不合法继续到右子树找就行了(比赛的时候心态崩了这么简单处理都不会了,要是冷静下来改改就过了)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+4;
const int inf=0x3f3f3f3f;
int tr[maxn<<2];
int a[maxn],b[maxn],vis[maxn];
void build(int o,int l,int r){
if(l==r)tr[o]=b[l];
else {
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
tr[o]=max(tr[o<<1],tr[o<<1|1]);
}
}
void update(int o,int l,int r,int p){
if(l==r)tr[o]=inf-1;
else {
int mid=l+r>>1;
if(p<=mid)update(o<<1,l,mid,p);
else update(o<<1|1,mid+1,r,p);
tr[o]=max(tr[o<<1],tr[o<<1|1]);
}
}
int ok=0;
int query(int o,int l,int r,int x,int y,int k){
//cout<<l<<" "<<r<<endl;
if(l==r){
//cout<<tr[o]<<endl;
if(tr[o]<=y){
return inf;
}
ok=1;
return l;
}
else {
int mid=l+r>>1;
int ans=inf;
if(k<=mid&&tr[o<<1]>y){
ans=query(o<<1,l,mid,x,y,k);
if(ans!=inf)return ans;
else if(ok==0){
return query(o<<1|1,mid+1,r,x,y,k);
}
}
else {
return query(o<<1|1,mid+1,r,x,y,k);
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int sz=n+2;
for(int i=1;i<=n+2;i++){
b[i]=inf-1;
}
for(int i=1;i<=n;i++)vis[i]=0;
for(int i=1;i<=n;i++)b[a[i]]=i;
build(1,1,sz);
int pos,r,k;
int ans=0;
while(q--){
int op;
ok=0;
scanf("%d",&op);
if(op==1){
scanf("%d",&pos);
pos=pos^ans;
if(vis[pos]==0)update(1,1,sz,a[pos]),vis[pos]=1;
}
else {
scanf("%d%d",&r,&k);
r=r^ans;
k=k^ans;
ans=query(1,1,sz,1,r,k);
printf("%d
",ans);
}
}
}
return 0;
}