Description
给出长度为N的序列A
M次询问,每次询问一个区间的Mex值
Mex(S)
M
e
x
(
S
)
表示在集合S中未出现过的最小自然数
强制在线
N,M≤200000
N
,
M
≤
200000
Solution
显然值大于n的数都是没用的
一个很自然的思路是主席树
如果我们直接用主席树维护每种数的出现次数,那我们在查找的时候是无法确定往哪边走的,因为要做减法再统计哪些位置为0
那我们可以直接维护下标
还是主席树,每个数记录它在这个位置之前最后一次出现的下标
一个区间就维护这些下标的最小值
最后要查询的时候,判断是否所有左儿子区间中的数都在查询区间中,是的话就要往右儿子走,否则就往左儿子走
时空复杂度 O(NlogN) O ( N log N )
Code
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
#define M 15000005
using namespace std;
int rt[N],t1,n,a[N],mi[M],t[M][2],n1,m;
void build(int k,int x,int l,int r,int w,int v)
{
if(l==r) mi[k]=v;
else
{
int mid=(l+r)>>1;
if(w<=mid) t[k][0]=++n1,t[k][1]=t[x][1],build(t[k][0],t[x][0],l,mid,w,v);
else t[k][0]=t[x][0],t[k][1]=++n1,build(t[k][1],t[x][1],mid+1,r,w,v);
mi[k]=min(mi[t[k][0]],mi[t[k][1]]);
}
}
int find(int k,int l,int r,int x)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(mi[t[k][0]]<x) return find(t[k][0],l,mid,x);
else return find(t[k][1],mid+1,r,x);
}
int main()
{
cin>>n>>m>>t1;
int mx=0;
fo(i,1,n)
{
scanf("%d",&a[i]);
rt[i]=++n1;
if(a[i]<=n+1) build(rt[i],rt[i-1],0,n+1,a[i],i);
else n1--,rt[i]=rt[i-1];
}
int ans=0;
fo(i,1,m)
{
int x,y;
scanf("%d%d",&x,&y);
if(t1) x^=ans,y^=ans;
ans=find(rt[y],0,n+1,x);
printf("%d\n",ans);
}
}