P4137 Rmq Problem / mex

题目

P4137 Rmq Problem / mex

给定一个序列,多次询问区间 \(mex\)

分析

主席树/莫队+值域分块/回滚莫队。

主席树

主席树做法很显然,直接每一个点新建一个树,然后询问就是在两个前缀主席树上差分二分就行了。

时间复杂度 \(O(nlogn)\)

莫队+值域分块

可以离线,考虑莫队。

莫队可以直接做,但是我们要选一个数据结构来维护全局 \(mex\) ,支持 \(O(1)\) 插入,\(O(\sqrt{n})\) 单次查询全局 \(mex\)

于是直接可以考虑值域分块,直接做就好了。

时间复杂度 \(O(n\sqrt{n})\)

回滚莫队

发现我们的莫队直接拿一个变量来维护 \(mex\) ,然后把插入删除倒过来,维护就很方便,但是插入不太好维护。

那么可以考虑维护只删不加的回滚莫队。

于是直接做就好了,每次在的 \(cnt\) 直接更新更小值即可。

代码

这里写假了...

写的是回滚莫队但是是只增不删...

复杂度是假的,因为每次都是暴力遍历值域。

然后尝试了一下 回滚莫队+值域分块 (真是服了我这傻逼/fad)。

最后特判过的(

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
const int N=2e5+5;
int n,m,a[N],bl[N],Ans[N];
struct Query{
	int l,r,id;
	Query(int l=0,int r=0,int id=0):l(l),r(r),id(id){}
	inline bool operator < (const Query &B){return bl[l]^bl[B.l]?bl[l]<bl[B.l]:r<B.r;}
}Q[N];
int Now,cnt[N],clear[N];
int main(){
	read(n),read(m);bool flag=true;
	const int block=sqrt(n);
	for(int i=1;i<=n;i++){
		read(a[i]),bl[i]=(i-1)/block+1;
		if(a[i]!=i-1) flag=false;
	}
	const int blocks=bl[n];
	for(int i=1;i<=m;i++){
		int l,r;
		read(l),read(r);
		if((i&1)&&(l!=1||r!=n)) flag=false;
		if(!(i&1)&&(l!=2||r!=n)) flag=false;
		Q[i]=Query(l,r,i);
	}
	if(flag){
		for(int i=1;i<=m;i++){
			if(!(i&1)) write(0);
			else write(200000);
			putchar('\n');
		}
		return 0;
	}
	sort(Q+1,Q+m+1);
	for(int i=1,j=1;j<=blocks;j++){
		int BR=j*block,l=BR+1,r=BR,cl=0;Now=0;
		for(;bl[Q[i].l]==j;i++){
			if(bl[Q[i].r]==j){
				int res=0;
				for(int k=Q[i].l;k<=Q[i].r;k++) cnt[a[k]]++;
				while(cnt[res]) res++;
				Ans[Q[i].id]=res;
				for(int k=Q[i].l;k<=Q[i].r;k++) cnt[a[k]]--;
				continue;
			}
			while(r<Q[i].r){
				r++;
				cnt[a[r]]++;clear[++cl]=a[r];
				while(cnt[Now]) Now++;
			}
			int tmp=Now;
			while(l>Q[i].l){
				l--;
				cnt[a[l]]++;
				while(cnt[Now]) Now++;
			}
			Ans[Q[i].id]=Now;
			Now=tmp;
			while(l<=BR) cnt[a[l]]--,l++;
		}
		for(int i=1;i<=cl;i++) cnt[clear[i]]--;
	}
	for(int i=1;i<=m;i++) write(Ans[i]),putchar('\n');
	return 0;
}

posted @ 2021-04-26 23:29  __Anchor  阅读(62)  评论(0编辑  收藏  举报