题意:
给出 个数的数组 ,其中 各不相同。
给出 个操作,分为 种:
- ,令 ;
- ,询问不等于 中任意一个数,且 的最小整数。
分析:
根据数组 建立权值线段树,里面存储各个权值在数组 的位置,由于询问的最小整数要不等于 中任意一个数,所以不等于 的权值的位置应初始化为 (如 ),这样就不会被排除。
查询时只需要查询权值 中最小的且位置 的权值,如果没找到,则答案一定是 (可以想象该情况就是数组 将 ~ 的权值恰好填满了)。
对于令 ,由于 足够大,则可以直接将 删去即可(令权值 的位置变为 )。
关键是在权值线段树中的查询操作,权值线段树记录 区间中的最大位置,这样就可以判断左、右区间内存不存在最大位置 。
遍历到线段树的一个结点,若左子树存在最大位置 ,则优先向左子树遍历,但是左子树不一定有答案(因为是左子树,所以位置 的权值不一定 );若左子树没答案,再判断右子树的是否存在最大位置 ,若存在,则答案一定在右子树(因为是左子树,所以位置 的权值一定 ),若不存在,则不要遍历右子树。
那么这样剪枝后,最坏的情况也只有 。
以下代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int MOD=998244353;
const int INF=0x3f3f3f3f;
const int maxn=1e5+50;
int n,m,a[maxn];
int t[maxn<<2];
void build(int rt,int l,int r)
{
if(l==r)
{
t[rt]=n+1;
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
void updata(int rt,int l,int r,int val,int pos)
{
if(l==r)
{
t[rt]=pos;
return;
}
int mid=(l+r)>>1;
if(val<=mid)
updata(rt<<1,l,mid,val,pos);
else
updata(rt<<1|1,mid+1,r,val,pos);
t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
int query(int rt,int l,int r,int k,int lim)
{
if(l==r)
{
if(t[rt]>lim)
return l;
else
return n+1;
}
int mid=(l+r)>>1;
int ans=n+1;
if(k<=mid&&t[rt<<1]>lim)
ans=query(rt<<1,l,mid,k,lim);
if(ans==n+1&&t[rt<<1|1]>lim)
ans=query(rt<<1|1,mid+1,r,k,lim);
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
build(1,1,n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
updata(1,1,n,a[i],i);
}
int op,pos,r,k;
int ans=0;
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&pos);
pos^=ans;
updata(1,1,n,a[pos],n+1);
}
else
{
scanf("%d %d",&r,&k);
r^=ans;
k^=ans;
ans=query(1,1,n,k,r);
printf("%d\n",ans);
}
}
}
return 0;
}