【BZOJ3585/3339】mex 莫队算法+分块

【BZOJ3585】mex

Description

  有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。

Input

  第一行n,m。
  第二行为n个数。
  从第三行开始,每行一个询问l,r。

Output

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

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

HINT

数据规模和约定
  对于100%的数据:
  1<=n,m<=200000
  0<=ai<=109
  1<=l<=r<=n

  对于30%的数据:

  1<=n,m<=1000

【BZOJ3339】Rmq Problem

题目一样,双倍经验

题解:依然直接上莫队+分块,发现虽然ai≤10^9,但是我们只需要判断它是否≥n,对于≥n的数我们直接领它为n就行了,因为n个自然数最多是0..n-1,出现了≥n的数就一定说明前面有空缺,所以不会对答案产生贡献

于是我们将自然数分块,维护块内不同自然数的个数和区间内自然数的出现次数,就没了~

又体会到了从0开始分块的美好~

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=200010;
int n,m,siz;
struct node
{
	int qa,qb,org;
}q[maxn];
int v[maxn],ans[maxn],s[maxn],sk[maxn];
bool cmp(node a,node b)
{
	if((a.qa-1)/siz==(b.qa-1)/siz)	return a.qb<b.qb;
	return (a.qa-1)/siz<(b.qa-1)/siz;
}
int main()
{
	scanf("%d%d",&n,&m);
	siz=(int)sqrt((double)n);
	int i,j;
	for(i=1;i<=n;i++)	scanf("%d",&v[i]),v[i]=(v[i]<=n)?v[i]:n;
	for(i=1;i<=m;i++)	scanf("%d%d",&q[i].qa,&q[i].qb),q[i].org=i;
	sort(q+1,q+m+1,cmp);
	int l=1,r=0;
	for(i=1;i<=m;i++)
	{
		while(r<q[i].qb)	r++,sk[v[r]/siz]+=(s[v[r]]==0),s[v[r]]++;
		while(r>q[i].qb)	s[v[r]]--,sk[v[r]/siz]-=(s[v[r]]==0),r--;
		while(l>q[i].qa)	l--,sk[v[l]/siz]+=(s[v[l]]==0),s[v[l]]++;
		while(l<q[i].qa)	s[v[l]]--,sk[v[l]/siz]-=(s[v[l]]==0),l++;
		for(j=0;j*siz+siz-1<=n&&sk[j]==siz;j++);
		for(j=j*siz;s[j]>0;j++);
		ans[q[i].org]=j;
	}
	for(i=1;i<=m;i++)	printf("%d
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/CQzhangyu/p/6805290.html