思维量比较大的一道题,如果之前做过类似的就很简单,但没做过的话真的有点难想。。
首先一个比较容易想到的点:
从1枚举到n+1,用某种方法计算最终的MEX序列是否出现i。
对于一个子数组[L,R],若其MEX等于x,则其必须满足:区间内不包含x,且区间内包含所有1 - x-1的数。
关键点在于这里如何进行维护。
我们可以这样做:
对于区间不好含x这个限制我们可以每次取区间:(lst[a[i]],i),这样区间一定不包含a[i],而包含1 - x-1的数可以考虑用权值线段树维护:
从左往右枚举,线段树维护当前状态下值为i的数,最后一次出现的位置。
这样我们求区间1 - x-1 的min,如果它大于lst[a[i]],说明区间(lst[a[i]],i)包含1 - x-1的所有数。
枚举更新即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
#define ls (o<<1)
#define rs (o<<1|1)
#define m (l+r)/2
#define pb push_back
typedef pair<int,int> pii;
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(int n){cnt=1;for(int i=0;i<=n;i++)head[i]=0;}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
int a[M],lst[M],vs[M];
int n;
int tr[M<<2];
void up(int o,int l,int r,int x,int d){
if(l==r){
tr[o]=d;
return ;
}
if(x<=m)up(ls,l,m,x,d);
else up(rs,m+1,r,x,d);
tr[o]=min(tr[ls],tr[rs]);
}
int qu(int o,int l,int r,int x,int y){
if(x<=l&&r<=y){
return tr[o];
}
int ans=1e9;
if(x<=m)ans=min(ans,qu(ls,l,m,x,y));
if(y>m)ans=min(ans,qu(rs,m+1,r,x,y));
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
//up(1,1,n,i,n+1);
//cout<<qu(1,1,n,1,1)<<" -- "<<endl;
for(int i=1;i<=n;i++){
if(a[i]==1){
vs[2]=1;
up(1,1,n,a[i],i);//单点赋值
continue;
}
vs[1]=1;
int L=lst[a[i]]+1,R=i-1;
// cout<<i<<" "<<L<<" -> "<<a[i]<<" "<<qu(1,1,n,1,a[i]-1)<<endl;
if(qu(1,1,n,1,a[i]-1)>=L)
vs[a[i]]=1;
up(1,1,n,a[i],i);//单点赋值
// cout<<"---> "<<qu(1,1,n,a[i],a[i])<<endl;
lst[a[i]]=i;
}
for(int i=2;i<=n+1;i++){
int L=lst[i]+1;
// cout<<" = "<<i<<" "<<L<<" -> "<<i<<" "<<qu(1,1,n,1,i-1)<<endl;
if(qu(1,1,n,1,i-1)>=L)
vs[i]=1;
}
for(int i=1;i<=n+7;i++){
if(!vs[i]){
cout<<i<<endl;
return 0;
}
}
return 0;
}
/*
9
5 4 3 2 1 2 3 4 5
*/