大致题意
给定一个1-n的排列,有m次操作。(n<=100000,m<100000);
操作1:给定pos=pos^lastans ,将a[pos]加1e7
操作2:给定r=r^ lastans,k=k^lastans ,询问大于等于k的最小的数,而且在区间1-r内不包含的数。
思路
因为强制在线,只能数据结构。一开始没看见是排列,想了半天…
然后考虑权值线段树,a[i]表示数值i出现的位置,b[i]表示第i个数是b[i];写一个线段树维护一下区间[l,r]的数值出现的最大位置。然后问题转化为,求区间[k,n]中最靠左的a[i]>r的位置。修改只要将b[i]改成INF就行。因为这是个排列,操作1 相当与是删除这个数字,所以将a[b[i]]=INF即可。
代码
跟普通线段树查询有点小区别,是查询叶节点位置。
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define maxm 1000006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
int t,n,m;
int a[maxn],b[maxn],ma[maxn];
int tr[maxn<<3];
void pushup(int x){tr[x]=max(tr[x<<1],tr[x<<1|1]);}
void build(int x,int l,int r)
{
if(l==r){
tr[x]=a[l];
return ;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
}
void update(int x,int l,int r,int pos)
{
if(l==r){
tr[x]=INF;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)update(x<<1,l,mid,pos);
else update(x<<1|1,mid+1,r,pos);
pushup(x);
}
int query(int x,int l,int r,int lp,int rp,int v)
{
if(l==r)return l;
int mid=(l+r)>>1;
//printf("l = %d r = %d\n",l,r);
int ans=INF;
if(lp<=mid&&tr[x<<1]>v)ans=min(ans,query(x<<1,l,mid,lp,rp,v));
if(ans<INF)return ans;
if(mid+1<=rp&&tr[x<<1|1]>v)ans=min(ans,query(x<<1|1,mid+1,r,lp,rp,v));
return ans;
}
int main()
{
t=read();
while(t--){
mem(tr);
n=read();m=read();
int x,y,cmd;
inc(i,1,n){
x=read();
b[i]=x;
a[x]=i;
}
inc(i,1,n)ma[i]=max(ma[i-1],b[i]);
build(1,1,n);
int ans=0;
inc(i,1,m){
cmd=read();
if(cmd==1){
x=read();
x^=ans;
update(1,1,n,b[x]);
}
else{
x=read();y=read();
x^=ans;y^=ans;
ans=query(1,1,n,y,n,x);
if(ans==INF)ans=ma[x]+1;
if(ans<=y)ans=y;
printf("%d\n",ans);
}
}
}
return 0;
}