P4137 Rmq Problem / mex
P4137 Rmq Problem / mex
Description
有一个长度为 \(n\) 的数组 \(\{a_1,a_2,\ldots,a_n\}\)。
\(m\) 次询问,每次询问一个区间内最小没有出现过的自然数。
Solutions
我们考虑主席树。首先由于数组值域很大,我们需要对数组进行离散化,但是发现有时候可以取到的 \(mex\) 值未必是 \(a_i\) 离散化之后的值,于是我们考虑将 \(0,a_i,a_i+1\) 离散化得到数组 \(a_i\),原排序去重后的数组记为 \(lst\)。
而后我们考虑建主席树,维护每一个权值在原数组中最后一次出现的下标。
对于每次查询操作 \(\operatorname{mex}([L,R])\),我们取出 \(root[r]\) 那棵树,并考虑树上二分找到下标小于 \(l\) 的最小权值 \(lst_l\) 即为答案。
当然本题也可以不离散化,因为显然如果 \(a_i>n\),那么一定对答案无贡献。
Code
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
const int N=1e7+7;
int n,m;
int cnt,ls[N],rs[N],__[N],lst[N],tot,a[N],root[N];
void modify(int pre,int &k,int l,int r,int x,int val){
	k=++cnt;
	ls[k]=ls[pre],rs[k]=rs[pre];
	if(l==r) return __[k]=val,void();
	if(x<=mid) modify(ls[pre],ls[k],l,mid,x,val);
	else modify(rs[pre],rs[k],mid+1,r,x,val);
	__[k]=min(__[ls[k]],__[rs[k]]);
}
int query(int k,int l,int r,int x){
	if(!k||l==r) return lst[l];
	if(__[ls[k]]>=x) return query(rs[k],mid+1,r,x);
	return query(ls[k],l,mid,x);
}
void debug(){
	for(int i=1;i<=tot;i++)	printf("%d ",lst[i]);
	puts("");
}
int main(){
//	freopen("data.in","r",stdin);
	scanf("%d%d",&n,&m);
	lst[++tot]=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]),lst[++tot]=a[i],lst[++tot]=a[i]+1;
	}
	sort(lst+1,lst+1+tot);
	tot=unique(lst+1,lst+tot+1)-lst-1;
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(lst+1,lst+1+tot,a[i])-lst;
		modify(root[i-1],root[i],1,tot,a[i],i);
	}
//	debug();
	while(m--){
		int l,r;
		scanf("%d%d",&l,&r);
		printf("%d\n",query(root[r],1,tot,l));
	}
	return 0;
}

                
            
        
浙公网安备 33010602011771号