CF1436E Complicated Computations

发布时间 2023-11-07 22:36:24作者: Candycar

CF1436E Complicated Computations

题目描述:

求一个数列的所有子区间的 mex 值的 mex

某个数组的 mex 是这个数组中没有包含的最小正整数。

数据范围:

\(1\leq n\leq 10^5,1\leq a_i\leq n\)

思路:

分析一下题目的流程,他先要你求出子区间的 mex,然后再求所有 mexmex
如果我们能够算出所有子区间的 mex 有哪些,则可以枚举求出 mex

所以我们考虑怎么解决第一个问题:
我们发现如果对每个子区间都去做一边 mex 显然是不优秀的,复杂度也不对。所以,我们尝试去计算有哪些区间的 mex

假设当前我们要求 \(x\) 能否作为 mex 出现,则我们不妨将整个序列通过 \(x\) 划分为若干段,若任意一段中包含的数的范围为 \(1\sim x-1\),则这一段的 mex\(x\)

考虑怎么维护这个东西。我们枚举 \(a_i\),如果 \(lst_{a_i}+1\sim i-1\) 这一段中包含了 \(1\sim a_i-1\),则这一段的 mex\(a_i\)。换言之,就是如果 \(1\sim a_i-1\) 最后出现的位置的最小值 \(\ge lst_{a_i}+1\),则这一段的 mex\(a_i\)

所以问题转换为单点修改,区间最小值。显然可以使用线段树解决。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define set(a,b) memset(a,b,sizeof(a))
#define ls p<<1
#define rs p<<1|1
#define pb push_back
#define pt putchar
#define All(a) a.begin(),a.end()
#define T int t;cin>>t;while(t--)
#define rand RAND
using namespace std;
char buf[1<<20],*p1,*p2;
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
template<class Typ> Typ &re(Typ &x){char ch=gc(),sgn=0; x=0;for(;ch<'0'||ch>'9';ch=gc()) sgn|=ch=='-';for(;ch>='0'&&ch<='9';ch=gc()) x=x*10+(ch^48);return sgn&&(x=-x),x;}
template<class Typ> void wt(Typ x){if(x<0) putchar('-'),x=-x;if(x>9) wt(x/10);putchar(x%10^48);}
const int inf=0x3f3f3f3f3f;
const int maxn=1e5+5;
const int mod=1e9+7;
int seed = 19243;
unsigned rand(){return seed=(seed*48271ll)%2147483647;}
int n;
int a[maxn];
int lst[maxn];
struct node{
	int mn;
}tree[maxn<<2];
int ban[maxn];
void push_up(int p){
	tree[p].mn=min(tree[ls].mn,tree[rs].mn);
}
void update(int l,int r,int x,int k,int p){
	if(l==r){
		tree[p].mn=k;
		return ;
	}
	int mid=l+r>>1;
	if(x<=mid)update(l,mid,x,k,ls);
	else update(mid+1,r,x,k,rs);
	push_up(p);
}
int query(int l,int r,int L,int R,int p){
	if(L<=l&&R>=r){
		return tree[p].mn;
	}
	int mid=l+r>>1;
	int res=inf;
	if(L<=mid)res=min(res,query(l,mid,L,R,ls));
	if(R>mid)res=min(res,query(mid+1,r,L,R,rs));
	return res;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		if(a[i]!=1)ban[1]=1;
		if(a[i]>1&&query(1,n,1,a[i]-1,1)>lst[a[i]])ban[a[i]]=1;
		lst[a[i]]=i;
		update(1,n,a[i],i,1);
	}
	for(int i=2;i<=n+1;i++){
		if(query(1,n,1,i-1,1)>lst[i])ban[i]=1;
	}
	for(int i=1;i<=n+2;i++)if(!ban[i]){
		cout<<i<<endl;
		return 0;
	}
	return 0;
}

补充:

法二:
我们常规求区间 mex 的时候一半都会使用莫队来解决这个问题。然后我们考虑人为的添加一些询问给这个题目。

则对于每个 \(a_i\),令 \(a_i\) 出现的次数分别为 \(p_1,p_2,\cdots,p_k\),则添加询问:\((1,p_1-1),(p_1+1,p_2-1),\cdots,(p_{k-1}+1,p_k-1),(p_k+1,n)\)
如果中间有一些询问的长度不够,就可以直接舍去。不难发现这个询问的次数不会超过 \(N\) 次。
所以总复杂度 \(O(N\sqrt N\log N)\)