个人主页:仍有未知等待探索-CSDN博客
目录
一、介绍
莫队算法就是一个离线的,通常针对于区间询问的一种算法。
二、莫队算法
莫队算法的核心就是分块思想。将一个数据分成若干段,这样便于以较小的区间移动次数来完成区间查询的操作。
一般的话,都是为分段点进行分块。然后对查询区间进行排序。
我们需要对区间进行维护,通过移动区间的左右端点使区间到某个特定的区间,完成查询。(一般也要我们去维护其他变量,来达到目的)
三、例题
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 500005;
int n, q;// 数组元素个数, q个询问
int a[maxn];// 数组元素
int l = 1, r = 0;// 要维护的区间左右端点
struct Q
{
int l, r, k, rk;// 查询区间的左右端点,分块编号,输入的顺序
int ans; // 记录在该查询区间内的答案
}query[maxn];
bool cmp1(struct Q x, struct Q y)
{
if (x.k == y.k) return x.r < y.r;
return x.l < y.l;
}
bool cmp2(struct Q x, struct Q y)
{
return x.rk < y.rk;
}
int main()
{
// 读数据
cin >> n >> q;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
// 计算分块点
int sz = sqrt(n);
// 存查询区间及其顺序
for (int i = 1; i <= q; i ++ )
{
cin >> query[i].l >> query[i].r;
// 分块是按照查询区间左端点分的
// 不是查询区间的顺序(没有意义)
query[i].k = query[i].l / sz;// 计算分块编号
query[i].rk = i; // 计算查询顺序
}
// 对查询区间进行排序,如果在同一个块内按右端点小的排,如果是不同块内按左端点小的排序
sort(query + 1, query + 1 + q, cmp1);
int l = 1, r = 0;// 自己维护的区间
int cnt[maxn] = {0};// 根据题意要维护的查询区间的元素出现的个数
int res = 0;// 记录答案
for (int i = 1; i <= q; i ++ )
{
// 加贡献
// 如果当前区间的左端点大于要查询的区间左端点,需要对区间进行扩张操作
while (l > query[i].l)
{
l -- ;
// 在新更新的左端点还没加入记录的时候,如果==1的话,加入的话就是2,满足条件,加入答案
if (cnt[a[l]] == 1) res ++ ;
// 在新更新的左端点还没加入记录的时候,如果==2的话,加入的话就是3,不满足条件,但是经历过==2的时候,已经被加入过条件,所以需要进行对答案进行 - 1
if (cnt[a[l]] == 2) res -- ;
// 更新cnt
cnt[a[l]] ++ ;
}
// 如果当前区间的右端点小于要查询的区间右端点,需要对区间进行扩张操作
while (r < query[i].r)
{
r ++ ;
if (cnt[a[r]] == 1) res ++ ;
if (cnt[a[r]] == 2) res -- ;
cnt[a[r]] ++ ;
}
// 减贡献
// 如果当前区间的左端点小于要查询的区间左端点,需要对区间进行缩小操作
while (l < query[i].l)
{
// 如果在还没缩小区间的时候,cnt==2,缩小后cnt==1,不符合答案条件,则对答案进行 -1
if (cnt[a[l]] == 2) res -- ;
// 如果在还没缩小区间的时候,cnt==3,缩小后cnt==2,符合答案条件,则对答案进行 +1
if (cnt[a[l]] == 3) res ++ ;
cnt[a[l]] -- ;
l ++ ;
}
// 如果当前区间的右端点大于要查询的区间右端点,需要对区间进行缩小操作
while (r > query[i].r)
{
if (cnt[a[r]] == 2) res -- ;
if (cnt[a[r]] == 3) res ++ ;
cnt[a[r]] -- ;
r -- ;
}
// 得出答案
query[i].ans = res;
}
sort(query + 1, query + 1 + q, cmp2);
for (int i = 1; i <= q; i ++ )
{
cout << query[i].ans << endl;
}
return 0;
}