[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;
}

  

时间: 2024-08-04 12:17:06

[BZOJ 3585] mex 【莫队+分块】的相关文章

BZOJ 3585 mex 莫队算法+分块

题目大意:给定一个长度为n的数组,m次询问某个区间内的mex值 怒写莫队233 将权值分成√n块,记录每个权值的出现次数以及每块内有多少权值出现过 修改O(1)即可完成 查询时首先扫一遍找到第一个块内有没有覆盖的点的块 然后在块内暴力查找 时间复杂度O(√n) 套个莫队 总时间复杂度O(m√n) #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include

[BZOJ 3236] [Ahoi2013] 作业 &amp;&amp; [BZOJ 3809] 【莫队 | 分块】

题目链接: BZOJ - 3236   BZOJ - 3809 算法一:莫队 首先,单纯的莫队算法是很好想的,就是用普通的第一关键字为 l 所在块,第二关键字为 r 的莫队. 这样每次端点移动添加或删除一个数字,用树状数组维护所求的信息就是很容易的.由于这里有 logn复杂度,所以复杂度还是挺高的. 于是 BZOJ-3236 的时限 100s,我的代码跑了 98s,险过...... However..BZOJ-3809 的出题人(SLYZ的神犇)就没有这么善良了!直接内存限制 28MB 就直接把

BZOJ 4129 Haruna’s Breakfast 带修改树上莫队+分块

题目大意:给定一棵树,每个点有一个非负点权,支持下列操作 1.修改某个点的点权 2.查询某条链上的mex 考虑链上不带修改的版本,我们可以用莫队+分块来搞(链接戳这里) 现在到了树上带修改,果断糖果公园 本来抱着逗比的心态写了一发结果1.4s过了 跟糖果公园的80s完全不成正比啊0.0 #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <

莫队+分块 BZOJ 3809

3809: Gty的二逼妹子序列 Time Limit: 80 Sec  Memory Limit: 28 MBSubmit: 1634  Solved: 482[Submit][Status][Discuss] Description Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题. 对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数. 为了方便,我们规定妹子们的美丽度全都在[1,n]中. 给定一个长度为n(1<=n<=100000)

BZOJ - 3339: Rmq BZOJ - 3585: mex

3339: Rmq Problem 3585: mex 题解:分块维护权值,用莫队转移. 分块修改操作$O(1)$,查询$O(\sqrt{A_{max}})$.莫队转移$O(m\sqrt n)$.总共是$O(m\sqrt n)$ 一份代码解决两道题.额外的经验! 1 #include<cmath> 2 #include<algorithm> 3 #include<cstdio> 4 #include<iostream> 5 using namespace s

HDU 5145 NPY and girls (莫队分块离线)

题目地址:HDU 5145 莫队真的好神奇..这样的复杂度居然只有n*sqrt(n)... 裸的莫队分块,先离线,然后按左端点分块,按块数作为第一关键字排序,然后按r值作为第二关键字进行排序.都是从小到大,可以证明这样的复杂度只有n*sqrt(n).然后进行块之间的转移. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <

【BZOJ 3809】 3809: Gty的二逼妹子序列 (莫队+分块)

3809: Gty的二逼妹子序列 Time Limit: 80 Sec  Memory Limit: 28 MBSubmit: 1728  Solved: 513 Description Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题. 对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数. 为了方便,我们规定妹子们的美丽度全都在[1,n]中. 给定一个长度为n(1<=n<=100000)的正整数序列s(1<=si<=n),对

Bzoj 3809: Gty的二逼妹子序列 莫队,分块

3809: Gty的二逼妹子序列 Time Limit: 35 Sec  Memory Limit: 28 MBSubmit: 868  Solved: 234[Submit][Status][Discuss] Description Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题. 对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数. 为了方便,我们规定妹子们的美丽度全都在[1,n]中. 给定一个长度为n(1<=n<=100000)的

bzoj 3809 Gty的二逼妹子序列 —— 莫队+分块

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3809 据说一开始应该想到莫队+树状数组,然而我想的却是莫队+权值线段树... 如果用权值线段树,则修改和查询都是 O(logn),总复杂度 O(n√nlogn),艰难...(而且仔细一看空间有点卡?) 看了TJ,才发现权值也可以分块,则查询 O(√n) 但修改 O(1),就可以过咯~ 代码如下: #include<iostream> #include<cstdio> #inc