Darkbzoj
SOL
法一:维护每一个值在原序列的最右出现位置的最小值,对于二分的左半部分,如果最小值大于等于当前左端点,说明已满。
用主席树排除右端点超出qr的影响。在主席树上二分
法二:增量法思想。 从[l,r] 到[l+1,r],只有a[l]可能能够拿来更新一段区间的mex,写一个以左端点为第一维的支持区间修改min,单点查询(可持久化标记)的主席树。
(也可以离线,用线段树做)
法三:可持久化trie(或者可持久化非旋treap),这样可以提出区间,再二分找点即可(类似法一)
CODE
#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define cs const
#define db double
#define ri register int
#define gc getchar()
#define in red()
inline int red(){
int num=0,f=1;char c=gc;
for(;!isdigit(c);c=gc)if(c=='-')f=-1;
for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
return num*f;
}
cs int N=1e7+10,inf=1e9,M=2e5+10;
int mex[M],nxt[M],las[M],vis[M],num[M],n,m;
int tag[N],tot,ch[N][2],rt[M];
#define lc(x) ch[x][0]
#define rc(x) ch[x][1]
inline int cpy(int x){++tot;tag[tot]=tag[x];ch[tot][0]=ch[x][0];ch[tot][1]=ch[x][1];return tot;}
inline void upt(int p,int l,int r,int ql,int qr,int &t,int v){
// cout<<v<<' '<<ql<<' '<<qr<<'\n';
// cout<<p<<'\n';
t=cpy(p);
if(ql>qr)return;
if(ql<=l&&r<=qr)return tag[t]=min(tag[t],v),void();
int mid=(l+r)>>1;
if(ql<=mid)upt(lc(p),l,mid,ql,qr,lc(t),v);
if(qr>mid)upt(rc(p),mid+1,r,ql,qr,rc(t),v);
}
int tmp;
inline void query(int p,int l,int r,int k){
// cout<<l<<' '<<r<<' '<<tag[p]<<'\n';
tmp=min(tag[p],tmp);
if(l==r)return;
int mid=(l+r)>>1;
if(k<=mid)query(lc(p),l,mid,k);
else query(rc(p),mid+1,r,k);
}
inline void build(int l,int r,int &t){
t=++tot;tag[t]=inf;
if(l==r)return tag[t]=mex[l],void();
int mid=(l+r)>>1;
build(l,mid,lc(t));build(mid+1,r,rc(t));
}
signed main(){
// freopen("data.in","r",stdin);
n=in;m=in;
for(ri i=1;i<=n;++i)num[i]=in;
int k=0;
for(ri i=1;i<=n;++i){
vis[num[i]]=1;
while(vis[k])++k;
mex[i]=k;
}
for(ri i=0;i<=200000;++i)las[i]=n+1;
for(ri i=n;i>=1;--i)nxt[i]=las[num[i]],las[num[i]]=i;
// for(ri i=1;i<=n;++i)cout<<nxt[i]<<'\n';
build(1,n,rt[1]);
for(ri i=2;i<=n;++i){
upt(rt[i-1],1,n,i,nxt[i-1]-1,rt[i],num[i-1]);
}
while(m--){
int l=in,r=in;
tmp=inf;
query(rt[l],1,n,r);
cout<<tmp<<'\n';
}
return 0;
}