传送门
题意:给你一个序列,每次查询【1,r】>=k 最小的没有出现的ai值。
解:就是一颗权值线段树,同时节点用ls维护他代表区间所有值出现位置的最大值,没有出现(或修改)都置成无穷大,查询的时候,走左右儿子根据k来,满足条件优先进入左儿子,但是要判断一下ls(lson)是否>r,如果小于r,那说明所有值都在小于r的区间出现了,就没必要进入。然后递归到叶子节点,出现的位置>r,那就说明答案是这个l了。在左儿子没有找到答案的话,在回溯的过程中,如果可以进入右儿子,那就说明答案一定会在右儿子区间中了,因为右儿子区间值>k且存在值的下标>r,所以只要找到下表>r最小的数即可。最坏的情况在树中走出的情况就是一个人形的样子,2*log。
#include<bits/stdc++.h>
#define il inline
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e7+5;
int a[maxn],b[maxn],ls[maxn];
int fg[maxn];
il void pushup(int rt){
ls[rt]=max(ls[rt<<1],ls[rt<<1|1]);
}
il void build(int l,int r,int rt){
if(l==r){
ls[rt]=b[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
il void update(int l,int r,int rt,int x){
if(l==r){
ls[rt]=b[l]=inf;
return ;
}
int mid=(l+r)>>1;
if(x<=mid) update(l,mid,rt<<1,x);
else update(mid+1,r,rt<<1|1,x);
pushup(rt);
}
int ans;
il void query(int l,int r,int rt,int R,int k){
if(l==r){
if(ls[rt]>R) ans=l;
return ;
}
int mid=(l+r)>>1;
if(k<=mid && ls[rt<<1]>R) query(l,mid,rt<<1,R,k);
if((k>mid || ans==inf) && ls[rt<<1|1]>R) query(mid+1,r,rt<<1|1,R,k);
}
int T,n,m;
int main(){
scanf("%d",&T);
for(int t=1;t<=T;++t){
int lans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
b[a[i]]=i;
}
for(int i=n+1;i<=n+10;++i) b[i]=inf;
build(1,n+10,1);
int op,t1,t2,r,k;
for(int i=1;i<=m;++i){
scanf("%d%d",&op,&t1);
if(op==1){
t1=t1^lans;
if(fg[t1]!=t) update(1,n+10,1,a[t1]),fg[t1]=t;
}
else{
scanf("%d",&t2);
r=t1^lans,k=t2^lans;
ans=inf;
query(1,n+10,1,r,k);
printf("%d\n",ans);
lans=ans;
}
}
}
return 0;
}