BZOJ 3585 Mex (莫队 + 分块)

Description
  有一个长度为n的数组{a1,a2,…,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。
  
Input
  第一行n,m。
  第二行为n个数。
  从第三行开始,每行一个询问l,r。

Output
  一行一个数,表示每个询问的答案。
  
Examples

Sample Input
5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5
Sample Output
1
2
3
0
3

Note
1 ≤ n , m ≤ 200000 1 \leq n,m \leq 200000 1n,m200000

0 ≤ a i ≤ 1 0 9 0 \leq a_i \leq 10^9 0ai109

1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1lrn

Solution
本题有多种解法,这里考虑莫队 + 分块
对权值分块,用莫队维护当前询问区间的每个数字出现次数以及每个块内出现的数字种数
每次暴力找到第一个没满的块,再进去暴力找到没出现的最小数字
复杂度 O ( n n ) O(n\sqrt{n}) O(nn )

Code

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define re register
using namespace std;
const int maxn = 2e5 + 5;

inline int read() {
    int num = 0, F = 1; char c = ' ';
    while (c < '0' || c > '9') F = (c == '-') ? -1 : 1, c = getchar();
    while (c >= '0' && c <= '9') num = (num << 1) + (num << 3) + c - '0', c = getchar();
    return num * F;
}
void print(int x) {
 	if (x < 0) {
		putchar('-');
		x = -x;
	}
	if (x > 9)
		print(x / 10);
	putchar(x % 10 + '0');
}

int n,m,blksiz, maxblk;
int a[maxn],blk[maxn], le[maxn],ri[maxn];
struct Query{
	int l,r,id,v;
}q[maxn];
bool cmp(Query x, Query y){return (x.v == y.v) ? (x.v&1) ? x.r < y.r : x.r > y.r : x.l < y.l;}
int cnt[maxn], bnum[maxn], siz[maxn];
inline void add(int pos){if(++cnt[a[pos]] == 1) bnum[blk[a[pos]]]++;}
inline void del(int pos){if(--cnt[a[pos]] == 0) bnum[blk[a[pos]]]--;}
int ans[maxn];
inline int get_ans(){
	for(re int i = 1;i <= maxblk;++i){
		if(siz[i] != bnum[i]){
			for(int j = le[i];j <= ri[i];++j) if(!cnt[j]) return j;
		}
	}
	return n;
}
int main(){
	n = read(), m = read();
	blksiz = (int)sqrt((double)n);
	for(re int i = 1;i <= n;++i){
		a[i] = read();
		if(a[i] > n) a[i] = n + 1;
	}
	for(re int i = 0;i <= n;++i){
		blk[i] = i / blksiz + 1;
	}
	maxblk = blk[n];
	for(re int i = 1;i <= maxblk;++i){
		le[i] = (i-1) * blksiz;
		ri[i] = i * blksiz - 1;
		siz[i] = ri[i] - le[i] + 1;
	}
	ri[maxblk] = n, siz[maxblk] = ri[maxblk] - le[maxblk] + 1; 
	for(re int i = 1;i <= m;++i){
		q[i].l = read(), q[i].r = read();
		q[i].id = i, q[i].v = blk[q[i].l];
	} sort(q + 1,q + 1 + m,cmp);
	for(re int i = 1, curL = 1, curR = 0;i <= m;++i){
		int L = q[i].l, R = q[i].r, id = q[i].id;
		while(curL < L) del(curL++);
		while(curL > L) add(--curL);
		while(curR > R) del(curR--);
		while(curR < R) add(++curR);
		ans[id] = get_ans();
	}
	for(re int i = 1;i <= m;++i){
		print(ans[i]);puts("");
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值