【回滚莫队】mex

题链

多次询问一个区间内最小没有出现过的自然数

回滚莫队:发现添加一个数难以维护,故只用删除操作来维护答案。用cnt[]来计数,一开始把[1,n]全部塞进去,记录全局答案ANS,只有新的询问所属的块与上次询问不同时更新ANS(因为该询问前面的块已经没有用了);

什么时候更新答案ans呢?一开始ans = ANS的,每一次删除的时候,判断cnt[x]是否等于0,因为一开始cnt[]数组是记录了 [ 询问左区间的块的左区间,n] 之间的所有数字的,所以删着删着如果cnt[x] == 0了,那么ans = min(ans,x);

详细看代码;

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ll long long
#define ULL unsigned long long
#define Pair pair<LL,LL>
#define f1 first
#define f2 second
#define ls rt<<1
#define rs rt<<1|1
#define Pi acos(-1.0)
#define eps 1e-6
#define DBINF 1e100
#define mod 998244353
#define MAXN 1e18
#define MS 1000009

LL n,m;
LL a[MS]; // 原数组 
struct node{
	int l,r,id;
}ask[MS];
LL size,bknum; // 每一块的位置,总块数 
LL bkl[MS],bkr[MS]; // 分别记录第i块的左右区间 
LL belong[MS]; // 表示下标pos位置所属块号 

LL ANS;
LL cnp[MS]; // 临时计数数组 用于暴力处理 cnp[i]表示i出现次数 
LL cnt[MS]; // 用于计数数组 cnt[i]表示i出现次数 

LL ac[MS]; // 记录答案 

void init_ANS(){
	for(int i=1;i<=n;i++){
		if(a[i] > n+1) continue;
		cnt[a[i]]++;
	}
	while(cnt[ANS]) ANS++;
}

void init_bk(){
	size = sqrt(n);
	bknum = n/size;
	for(int i=1;i<=bknum;i++){
		bkl[i] = (i-1)*size+1;
		bkr[i] = i*size; 
	}
	if(bkr[bknum] < n){
		bknum++;
		bkl[bknum] = bkr[bknum-1]+1;
		bkr[bknum] = n;
	}
	for(int i=1;i<=bknum;i++){
		for(int j=bkl[i];j<=bkr[i];j++){
			belong[j] = i;
		}
	}
}

bool cmp(node t1,node t2){ // 按块排序,左区间从小到大,右区间从大到小 
	if(belong[t1.l] != belong[t2.l]) return t1.l < t2.l;
	return t1.r > t2.r;
}

void add(int pos){ 
	if(a[pos] > n+1) return;
	cnt[a[pos]]++;
}

void remove(int pos,LL &ans){ // 删除时更新答案 
	if(a[pos] > n+1) return;
	cnt[a[pos]]--;
	if(cnt[a[pos]] == 0) ans = min(ans,a[pos]);
}

int main() {
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i=1;i<=n;i++){
		cin >> a[i];
	}
	init_ANS(); // 初始化全局答案 
	for(int i=1;i<=m;i++){
		int l,r;
		cin >> l >> r;
		ask[i] = {l,r,i};
	}
	init_bk(); // 初始化 块 
	sort(ask+1,ask+m+1,cmp); // 对询问按 块 排序 
	
	int L = 1 ,R = n;
	int lastbk = 0;
	LL ans = ANS;
	for(int i=1;i<=m;i++){
		if(belong[ask[i].l] == belong[ask[i].r]){ // 属于同一块 暴力处理 
			for(int j=ask[i].l;j<=ask[i].r;j++){
				if(a[j] > n+1) continue;
				cnp[a[j]]++;
			}
			LL tmp = 0;
			while(cnp[tmp]) tmp++;
			ac[ask[i].id] = tmp;
			for(int j=ask[i].l;j<=ask[i].r;j++){
				if(a[j] > n+1) continue;
				cnp[a[j]]--;
			}
			continue;
		}
		if(belong[ask[i].l] != lastbk){ // 新的左区间与上次的左区间块不同 
			while(R < n) add(++R);
			while(L < bkl[belong[ask[i].l]]) remove(L++,ANS); // ***更新全局ANS 
			lastbk = belong[ask[i].l];
			ans = ANS;
		}
		while(R > ask[i].r){
			remove(R--,ans);
		}
		LL tmp = ans; // 左区间可能是乱序的,tmp作为临时记录 
		while(L < ask[i].l){
			remove(L++,tmp);
		}
		ac[ask[i].id] = tmp;
		// 回滚 
		while(L > bkl[belong[ask[i].l]]) add(--L);
	}
	for(int i=1;i<=m;i++){
		cout << ac[i] << "\n";
	}
	
	return 0;
}
posted @ 2021-05-11 11:53  棉被sunlie  阅读(94)  评论(0)    收藏  举报