莫队算法是一个用分块实现的暴力算法,只支持离线区间操作。时间复杂度 O ( n*sqrt(n) )
算法思路参考:https://www.cnblogs.com/WAMonster/p/10118934.html
主要就是先对查询的区间排序,先按左端点所在块从小到大排序,再按右端点从小到大排序。
查询时用 L 和 R 来扫描头尾,初始L=1,R=0;
一个数组,两个查询区间Q1和Q2,求这两个区间分别存在多少种不同的数字.
下图是参考博客里的,可以帮助理解。
跳过几步之后。。。
第一个区间查询完毕,开始查询第二个区间,同样开始移动L和R
移动L,删去1,但是我们发现1不只出现了一次,所以总数不变。
当L移动到4时,删除的数出现次数都不为1,总数不变.
下图总数应为4,写错了
第二个区间查询完毕。
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
using namespace std;
const int inf =0x3f3f3f3f;
const int maxn = 1e5 + 5;
typedef long long ll;
struct node
{
int l, r, id;
} p[maxn << 1];
int pos[maxn];//分块
int vis[maxn]; //可以记录数据次数或者其他
int ans[maxn]; //记录答案
bool cmp(node a, node b)
{
if (pos[a.l] == pos[b.l])
return a.r < b.r;
return pos[a.l] < pos[b.l];
}
int res, a[maxn]; //res记录总数
void add(int i) //根据题意修改这两个模块
{
//增加
}
void del(int i)
{
//删除
}
int main()
{
int n, q;
cin >> n >> q;
int dis = sqrt(n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), pos[i] = i / dis; //分块
for (int i = 0; i < q; i++)
{
scanf("%d %d", &p[i].l, &p[i].r);
p[i].id = i;
}
sort(p, p + q, cmp);
int l = 1, r = 0;
for (int i = 0; i < q; i++)
{
while (r < p[i].r)
add(++r);
while (r > p[i].r)
del(r--);
while (l < p[i].l)
del(l++);
while (l > p[i].l)
add(--l);
ans[p[i].id] = res;
}
for (int i = 0; i < q; i++)
printf("%d\n", ans[i]);
}
思路就是这样,我们来看个题练下吧。
题意:求某个区间内数字的值和出现次数相同的数字个数
输入:
第一行包含两个空格分隔的整数n和m(1≤n, m≤10^5)——数组a的大小和对它的查询数。下一行包含n个用空格分隔的正整数a1 a2…, an(1≤ai≤10^9)。接下来的m行包含查询的描述,每行一个。这些行的第j个包含了第j个查询的描述,它是两个以空格分隔的整数lj和rj(1≤lj≤rj≤n)。
输出
在m行中打印m个整数——查询的答案。第j行应该包含第j个查询的答案。
例:
7 2 3 1 2 2 3 3 7 1 7 3 4
3 1
思路:vis数组记录出现次数,如果vis[ i ]=i,总数+1,如果vis[ i ] = i +1,代表之前它是符合要求的,总数+1过,但是现在又不符合要求了,所以总数-1;
本题数据需要离散化。
代码:
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
using namespace std;
const int inf =0x3f3f3f3f;
const int maxn = 1e5 + 5;
typedef long long ll;
struct node
{
int l, r, id;
} p[maxn << 1];
vector<int>v;
int pos[maxn];//分块
int vis[maxn]; //可以记录数据次数或者其他
int ans[maxn]; //记录答案
int res=0, a[maxn],b[maxn]; //res记录总数
int getid(int x) //离散化
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
bool cmp(node a, node b)
{
if (pos[a.l] == pos[b.l])
return a.r < b.r;
return pos[a.l] < pos[b.l];
}
void add(int i) //根据题意修改这两个模块
{
vis[b[i]]++;
if(vis[b[i]]==a[i])
res++;
else if(vis[b[i]]-1==a[i]) //如果加上这个点之后不符合要求并且没加上这个点时符合要求的话,总数减1
res--;
}
void del(int i)
{
vis[b[i]]--;
if(vis[b[i]]==a[i])
res++;
else if(vis[b[i]]+1==a[i]) //如果减去这个点之后不符合要求并且没减去这个点时符合要求的话,总数减1
res--;
}
int main()
{
int n, q;
cin >> n >> q;
int dis = sqrt(n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), pos[i] = i / dis,v.push_back(a[i]); //分块
sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end()); //离散化
for (int i = 0; i < q; i++)
{
scanf("%d %d", &p[i].l, &p[i].r);
p[i].id = i;
}
for(int i=1; i<=n; i++)
b[i]=getid(a[i]);
sort(p, p + q, cmp);
int l = 1, r = 0;
for (int i = 0; i < q; i++)
{
while (r < p[i].r)
add(++r);
while (r > p[i].r)
del(r--);
while (l < p[i].l)
del(l++);
while (l > p[i].l)
add(--l);
ans[p[i].id] = res;
}
for (int i = 0; i < q; i++)
printf("%d\n", ans[i]);
}