莫队算法,就是巧妙处理区间问题的一种算法。
举个例子来讲一下
问题:有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;
}