codeforces 1436E - Complicated Computations (思维 + 线段树)
题目链接:https://codeforces.com/problemset/problem/1436/E
对于一个答案\(x\),考虑\(x\)可以是子区间\(mex\)值的条件:
首先\(x\)不能在子区间中出现过,所以可以将序列划分成以\(x\)为分割点的子区间
其次是在这样的子区间中,\([1,x-1]\)全部都出现过
令 \(lst[i]\) 表示 \(i\) 最后一次出现的位置,那么每扫到一个新元素\(a[i]\),就判断一下\([i,a[i]-1]\)最后一次出现的位置
是不是都比\(lst[i]\)大,也即是否都出现在了这段子区间中
可以使用线段树实现,线段树维护的是元素最后一次出现的位置
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int n,cnt,N;
int a[maxn],lst[maxn];
bool mex[maxn];
struct SEG{
int mi,add;
}t[maxn<<2];
void pushup(int i){ t[i].mi = min(t[i<<1].mi,t[i<<1|1].mi); }
void mdf(int i,int l,int r,int p,int k){
if(l == r){
t[i].mi = k;
return;
}
int mid = (l+r)/2;
if(p<=mid) mdf(i<<1,l,mid,p,k);
else mdf(i<<1|1,mid+1,r,p,k);
pushup(i);
}
int qry(int i,int l,int r,int x,int y){
if(x <= l && r <= y){
return t[i].mi;
}
int mid = (l+r)/2;
int mi = 1000000007;
if(x <= mid) mi = min(mi,qry(i<<1,l,mid,x,y));
if(y > mid) mi = min(mi,qry(i<<1|1,mid+1,r,x,y));
return mi;
}
ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
int main(){
n = read();
for(int i=1;i<=n;++i){
a[i] = read();
if(a[i] != 1) mex[1] = 1; // 对 1 特判
mdf(1,1,n,a[i],i);
if(a[i] > 1 && qry(1,1,n,1,a[i]-1) > lst[a[i]]) mex[a[i]] = 1;
lst[a[i]] = i;
}
for(int i=2;i<=n+1;++i){ if(qry(1,1,n,1,i-1) > lst[i]) mex[i] = 1; } // 处理最后一段区间
int ans = 1;
for(;mex[ans] && ans <= n+1;) ++ans;
printf("%d\n",ans);
return 0;
}