下笔春蚕食叶声。

题解 CF1436E 【Complicated Computations】

题意:

n个数,对所有子区间求mex,

把答案组成新的一列数,再求mex。

分析:

先加入数值 \(∈[1,i]\) 的数,

然后查询是否存在符合条件的区间有i-1个种数。

条件:“两个为i的数之间”或“到a[i]的前缀”
或“从a[i]到结尾”

(有i-1种,说明有连续[1,i-1],mex可能是i,不可能是答案)。


用线段树解决。

t[x]保存的是x最后一次出现的地方,线段树维护区间最小值。

扫到a[i]的时候,设m为[1,a[i]-1]中,出现最早的数所在的位置。

如果m>上一个a[i]出现的位置,

就说明存在两个为a[i]的数之间有a[i]-1个数,mex可以为a[i]。

这样就解决了。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define rd(x) scanf("%d",&x);
typedef long long LL;
const int N=1e5+10;
int n,a[N],t[N<<2],las[N],f[N]; 
void pushup(int rt){ t[rt]=min(t[rt<<1],t[rt<<1|1]); }
int query(int rt,int l,int r,int L,int R){
//	cout<<rt<<" "<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
	if(l>R||r<L) return 1e9;
	if(L<=l&&r<=R){
	//	cout<<rt<<"*"<<t[rt]<<endl;
		return t[rt];
	}
	int mid=l+r>>1;
	return min(query(rt<<1,l,mid,L,R),query(rt<<1|1,mid+1,r,L,R));
}
void update(int rt,int l,int r,int x,int val){
//	cout<<rt<<"*"<<l<<" "<<r<<" "<<x<<" "<<val<<endl;
	if(x<l||x>r) return;
	if(l==r) { t[rt]=val; return; }
	int mid=l+r>>1;
	update(rt<<1,l,mid,x,val);
	update(rt<<1|1,mid+1,r,x,val);
	pushup(rt);
	return;
}
int main(){
	rd(n);
	rep(i,1,n){
		rd(a[i]);
		int x=0; if(a[i]!=1) x=query(1,1,n,1,a[i]-1);
	//	cout<<a[i]<<" "<<x<<endl;
        //[1,a[i]-1]中最早出现的数(每次维护的都是每个数最晚出现的地点)晚于于lst[a[i]]
		//两个a[i]之间有集齐i-1个
		// 存在mex为a[i]的子区间
		if(x>las[a[i]]) f[a[i]]=1;
		update(1,1,n,a[i],i);las[a[i]]=i;
	}
	rep(i,2,n+1){
		int x=query(1,1,n,1,i-1);
		if(x>las[i]) f[i]=1;
	}//结尾再统计一次 
	rep(i,1,n) if(a[i]!=1) f[1]=1;
	int i=1;
	while(1){
		if(f[i]==0){
			printf("%d\n",i);
			return 0;
		}
		i++;
	}
	return 0;
}

posted @ 2020-10-27 08:29  ACwisher  阅读(265)  评论(0编辑  收藏  举报