莫队算法(离线处理区间问题)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MMMMMMMW/article/details/81090963

莫队算法,就是巧妙处理区间问题的一种算法。

举个例子来讲一下

问题:有n个数组成一个序列,有m个形如询问L, R的询问,每次询问需要回答区间内至少出现2次的数有哪些。

一、暴力法

m次询问,每次询问循环n次,时间复杂度O(m*n)

二、巧妙点的

我们可以通过前面区间来解决后面区间的问题

比如已知[1,10],那么[1,11]就只需要再访问一次即可,而暴力却必须11次。

设置一个L,R,分别代表此刻区间的左边界和右边界。

第一次询问:[1,2],此时L = 1,R = 2

第二次询问:[1,10],此时L不变,R+8

第三次询问:[10,100],此时L+9,R+90

..........

通过这个我们就可以很巧妙得利用前面区间的内容来解决问题,但如果出现一些变态的数据,就会导致L,R来回大幅度移动,还是很容易超时

三、莫队算法
中国ACM大佬自创的算法,采用的是分块排序来将区间先排好,其他与算法二差不多。

假设有n个数据,那么每个块的数量就是根号n(这样取块效率最高)

之后,先排左区间的块,若块大小相同,再排右区间的值。

例题:

HH的项链

HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一

段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此他的项链变得越来越长。有一天,他突然提出了一

个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只

好求助睿智的你,来解决这个问题。

Input

第一行:一个整数N,表示项链的长度。 

第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。 

第三行:一个整数M,表示HH询问的个数。 

接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

N ≤ 50000,M ≤ 200000。

Output

M行,每行一个整数,依次表示询问对应的答案。

Sample Input

6 1 2 3 4 3 5 3 1 2 3 5 2 6

Sample Output

2 2 4

附上AC代码:

#include <cstdio>  
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <list>
#include <map>
#include <stack>
#include <queue>
using namespace std;
#define ll long long
const int maxn1 = 1000005;
int cur[maxn1];
int then[maxn1];
int ans[maxn1];
int limit,n,m;
struct node
{
	int l,r,id;
}que[maxn1];
bool cmp(node x,node y)
{
	if(x.l/limit == y.l/limit)
		return x.r < y.r;
	return x.l/limit < y.l/limit;
}
void init()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;i++)
		scanf("%d",&cur[i]);
	scanf("%d",&m);
	for(int i = 1;i <= m;i++)
	{
		scanf("%d%d",&que[i].l,&que[i].r);
		que[i].id = i;
	}
	limit = (int)(sqrt(n)+0.5);
	memset(then,0,sizeof(then));
	sort(que+1,que+1+m,cmp);
	//初始化
}
void solve()
{
	int L,R,ans1;
	L = R = 0;
	ans1 = 0;
	for(int i = 1;i <= m;i++)
	{
		while(que[i].l > L)
		{
			then[cur[L]]--;
			if(then[cur[L]] == 0)
				ans1--;
			L++;
		}
		//减
		while(que[i].r < R)
		{
			then[cur[R]]--;
			if(then[cur[R]] == 0)
				ans1--;
			R--;
		}
		//减
		while(que[i].l < L)
		{
			L--;
			then[cur[L]]++;
			if(then[cur[L]] == 1)
				ans1++;
		}
		//加
		while(que[i].r > R)
		{
			R++;
			then[cur[R]]++;
			if(then[cur[R]] == 1)
				ans1++;
		}
		//加
		ans[que[i].id] = ans1;
	}
	for(int i = 1;i <= m;i++)
		printf("%d\n",ans[i]);
}	
int main()
{
	init();
	solve();
	//cout << "AC" <<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/MMMMMMMW/article/details/81090963