思路:线段树。
对操作2分析可知: a n s ∈ [ 1 , n + 1 ] ans\in[1,n+1] ans∈[1,n+1]
操作1相当于:将一个数变得很大就可以直接将 a [ p o s ] a[pos] a[pos]的下标置为 n + 1 n+1 n+1,因为此时 a [ p o s ] a[pos] a[pos]的值肯定满足大于等于 k k k,而且原来的 a [ p o s ] ≥ r a[pos]\ge r a[pos]≥r就肯定能被取到。
因此考虑建立权值线段树储存值,然后用一个数组维护一个区间的最大下标 m x [ x ] mx[x] mx[x]。
区间查询 查找最大下标大于 r r r,且值大于等于 k k k的最小值。
每次单点更新就将 m x [ x ] = n + 1 mx[x]=n+1 mx[x]=n+1。
#include
using namespace std;
typedef long long ll;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair
#define fi first
#define se second
#define pb push_back
#define il inline
int id[N],t,n,m,a[N];
struct SegTree{
int mx[N<<2];
void re(int x){
mx[x]=max(mx[lx],mx[rx]);
}
void build(int x,int l,int r){
//printf("x=%d,(%d,%d)\n",x,l,r);
if(l==r){
mx[x]=id[l];return;
}
int mid=l+r>>1;
build(lx,l,mid);
build(rx,mid+1,r);
re(x);
}
void upd(int x,int l,int r,int val){
if(l==r){
mx[x]=n+1;
return;
}
int mid=l+r>>1;
if(val<=mid) upd(lx,l,mid,val);
else upd(rx,mid+1,r,val);
re(x);
}
int query(int x,int l,int r,int R,int K){
if(r<K) return -1;
if(l==r) return l;
int ans=-1,mid=l+r>>1;
if(mx[lx]>R) ans=query(lx,l,mid,R,K);
if(ans==-1&&mx[rx]>R) ans=query(rx,mid+1,r,R,K);
return ans;
}
}T;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
id[a[i]]=i;
}
id[n+1]=n+1;
T.build(1,1,n+1);
int lst=0;
while(m--){
int op,x,y;
scanf("%d",&op);
if(op==1){
scanf("%d",&x),x^=lst;
T.upd(1,1,n+1,a[x]);
}
else {
scanf("%d%d",&x,&y);
x^=lst,y^=lst;
lst=T.query(1,1,n+1,x,y);
printf("%d\n",lst);
}
}
}
return 0;
}