[BZOJ 3585] mex 【莫队+分块】

题目链接:BZOJ – 3585

 

题目分析

区间mex,即区间中没有出现的最小自然数。

那么我们使用一种莫队+分块的做法,使用莫队维护当前区间的每个数字的出现次数。

然后求mex用分块,将权值分块(显然mex 一定小于等于 n ,大于 n 的权值没有意义,可以直接忽略),每块大小 sqrt(n) 。

然后区间中的某个数的数量被减到0的时候就将它所在的块的种类计数减一,添加数的时候类似。

然后枚举每个块,找到最小的中间有数不存在的块(即种类数小于块中的数的种数),然后到这个快里直接从小一个一个找到第一个不存在的数。

这样莫队的复杂度和分块的复杂度是相加的, O(n^1.5) + O(n^1.5) = O(n^1.5) 。

 

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

const int MaxN = 200000 + 5, MaxQ = 200000 + 5, MaxB = 500 + 5;

int n, m, BlkSize, MaxBlk;
int A[MaxN], Blk[MaxN], L[MaxB], R[MaxB], Ans[MaxQ], Cnt[MaxN], V[MaxB], Size[MaxB];

struct Query
{
	int Idx, l, r, v;
} Q[MaxQ];

inline bool Cmp(Query q1, Query q2)
{
	if (q1.v == q2.v) return q1.r < q2.r;
	return q1.v < q2.v;
}

inline void Del_Num(int x)
{
	--Cnt[x];
	if (Cnt[x] == 0) --V[Blk[x]];
}

inline void Add_Num(int x)
{
	if (Cnt[x] == 0) ++V[Blk[x]];
	++Cnt[x];
}

void Pull(int f, int x, int y)
{
	if (x == y) return;
	if (f == 0)
	{
		if (x < y)
		{
			for (int i = x; i < y; ++i)
				Del_Num(A[i]);
		}
		else
		{
			for (int i = x - 1; i >= y; --i)
				Add_Num(A[i]);
		}
	}
	else
	{
		if (x < y) 
		{
			for (int i = x + 1; i <= y; ++i)
				Add_Num(A[i]);
		}
		else
		{
			for (int i = x; i > y; --i)
				Del_Num(A[i]);
		}
	}
}

int Get_Ans()
{
	int ret = n;
	for (int i = 1; i <= MaxBlk; ++i)
		if (V[i] < Size[i])
		{
			for (int j = L[i]; j <= R[i]; ++j)
				if (Cnt[j] == 0)
				{
					ret = j;
					break;
				}
			break;
		}
	return ret;
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%d", &A[i]);
		if (A[i] > n) A[i] = n + 1;
	}
	BlkSize = (int)sqrt((double)n);
	for (int i = 0; i <= n; ++i) Blk[i] = i / BlkSize + 1;
	MaxBlk = Blk[n];
	for (int i = 1; i <= MaxBlk; ++i)
	{
		L[i] = (i - 1) * BlkSize;
		R[i] = i * BlkSize - 1;
		Size[i] = R[i] - L[i] + 1;
	}
	R[MaxBlk] = n;
	Size[MaxBlk] = n - L[MaxBlk] + 1;
	for (int i = 1; i <= m; ++i)
	{
		scanf("%d%d", &Q[i].l, &Q[i].r);
		Q[i].Idx = i;
		Q[i].v = Q[i].l / BlkSize;
	}
	sort(Q + 1, Q + m + 1, Cmp);
	for (int i = Q[1].l; i <= Q[1].r; ++i) Add_Num(A[i]);
	Ans[Q[1].Idx] = Get_Ans();
	for (int i = 2; i <= m; ++i)
	{
		if (Q[i].r <= Q[i - 1].l) 
		{
			Pull(0, Q[i - 1].l, Q[i].l);
			Pull(1, Q[i - 1].r, Q[i].r);
		}
		else
		{
			Pull(1, Q[i - 1].r, Q[i].r);
			Pull(0, Q[i - 1].l, Q[i].l);
		}
		Ans[Q[i].Idx] = Get_Ans();
	}
	for (int i = 1; i <= m; ++i) printf("%d\n", Ans[i]);
	return 0;
}

  

    原文作者:JoeFan
    原文地址: https://www.cnblogs.com/JoeFan/p/4422711.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞