莫队算法特点:离线,时间复杂度n1.5
- 对数组进行分块,分块的大小是n0.5,数组被分成了n0.5块。
- 将所有查询按第一关键字为左边界的分块索引,第二关键字为右边界进行排序。详见代码cmp函数。
- 排序完成以后,离线回答每个询问。
在模板基础上,一般是修改add和del函数,指针移动时,区间维护内容稍有不同,详见下面的两个题目。
题目一:SPOJ DQUERY - D-query
Given a sequence of n numbers a1, a2, …, an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, …, aj.
输入描述:
Line 1: n (1 ≤ n ≤ 30000).
Line 2: n numbers a1, a2, …, an (1 ≤ ai ≤ 106).
Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).
输出描述:
For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, …, aj in a single line.
输入样例:
5
1 1 2 1 3
3
1 5
2 4
3 5
输出样例:
3
2
3
大概思路:
cnt[a[x]]表示x位置上的数值a[x]在两个指针之间出现的次数,当次数在0与1之间转换时,不同数字个数s改变。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=3e4+20,M=1e6+20,Q=2e5+20;
int n,m,cl=1,cr,bl,a[N],res[Q],s,cnt[M];
struct node{
int l,r,id;
}t[Q];
int cmp(const node &s1,const node &s2){//莫队灵魂所在
return (s1.l/bl==s2.l/bl)?(s1.r<s2.r):(s1.l<s2.l);
}
inline void add(int x){//指针移动区间扩大
if(!cnt[a[x]]) s++;
cnt[a[x]]++;
}
inline void del(int x){//指针移动区间缩小
cnt[a[x]]--;
if(!cnt[a[x]]) s--;
}
signed main(){
scanf("%d",&n); bl=sqrt(n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%d %d",&t[i].l,&t[i].r),t[i].id=i;
sort(t+1,t+1+m,cmp);//查询离线
for(int i=1;i<=m;i++){//按块移动指针
int L=t[i].l; int R=t[i].r;
while(cr<R) add(++cr);
while(cl>L) add(--cl);
while(cr>R) del(cr--);
while(cl<L) del(cl++);
res[t[i].id]=s;
}
for(int i=1;i<=m;i++) printf("%d\n",res[i]);
return 0;
}
题目二:CF Little Elephant and Array
The Little Elephant loves playing with arrays. He has array a, consisting of n positive integers, indexed from 1 to n. Let’s denote the number with index i as ai.
Additionally the Little Elephant has m queries to the array, each query is characterised by a pair of integers lj and rj (1 ≤ lj ≤ rj ≤ n). For each query lj, rj the Little Elephant has to count, how many numbers x exist, such that number x occurs exactly x times among numbers alj, alj + 1, …, arj.
Help the Little Elephant to count the answers to all queries.
输入描述:
The first line contains two space-separated integers n and m (1 ≤ n, m ≤ 105) — the size of array a and the number of queries to it. The next line contains n space-separated positive integers a1, a2, …, an (1 ≤ ai ≤ 109). Next m lines contain descriptions of queries, one per line. The j-th of these lines contains the description of the j-th query as two space-separated integers lj and rj (1 ≤ lj ≤ rj ≤ n).
输出描述:
In m lines print m integers — the answers to the queries. The j-th line should contain the answer to the j-th query.
输入样例:
7 2
3 1 2 2 3 3 7
1 7
3 4
输出样例:
3
1
大概思路:
cnt[a[x]]表示x位置上的数值a[x]在两个指针之间出现的次数,当次数在a[x]上下转换时,答案s改变。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,cl=1,cr,bl,a[N],res[N],s,cnt[N];
struct node{
int l,r,id;
}t[N];
int cmp(const node &s1,const node &s2){
return (s1.l/bl==s2.l/bl)?(s1.r<s2.r):(s1.l<s2.l);
}
inline void add(int x){
++cnt[a[x]]; s+=(cnt[a[x]]==a[x]); s-=(cnt[a[x]]==a[x]+1);
}
inline void del(int x){
--cnt[a[x]]; s+=(cnt[a[x]]==a[x]); s-=(cnt[a[x]]+1==a[x]);
}
signed main(){
scanf("%d %d",&n,&m); bl=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]>n)a[i]=N-1;
}
for(int i=1;i<=m;i++) scanf("%d %d",&t[i].l,&t[i].r),t[i].id=i;
sort(t+1,t+1+m,cmp);
for(int i=1;i<=m;i++){
int L=t[i].l; int R=t[i].r;
while(cr<R) add(++cr);
while(cl>L) add(--cl);
while(cr>R) del(cr--);
while(cl<L) del(cl++);
res[t[i].id]=s;
}
for(int i=1;i<=m;i++) printf("%d\n",res[i]);
return 0;
}