一.关于莫队
一种离线的关于区间操作的算法。
一般莫队会和以下知识点一起使用:
1.分块思想。
2.离散化。
3.树链剖分/倍增,用于应付树上的(然而本蒟蒻写这篇博客时还不会,后续补充)
二.算法实现
1.SPOJ 3267 D-Query
暴力做法:记录每个数字出现的次数,每个询问统计出现区间内次数不为0的数字个数。
复杂度:O(n2)
显然过不去。
考虑优化方法。每次遇到新数那么它出现次数一定是0,遇到旧数如果后面没有了那么它出现次数一定是1。
用双指针扫区间?
对于每个询问区间用指针移动来代替枚举即可实现上面的优化。
如果区间特别多怎么办?
考虑分块,按照块的下标和每个询问的右端点排序,那么就避免了双指针扫过去又扫回来的情况。
这就是莫队的基本操作了。
总复杂度:O(nlogn)+O(n√n)+O(n√n)=O(n√n)
同时这是一道莫队裸题。本题AC代码:(莫队模板)

1 #include<bits/stdc++.h>
2 using namespace std;
3 struct Query
4 {
5 int l,r,num,pos;
6 bool operator < (const Query &k) const
7 {
8 if(pos==k.pos) return r<k.r;
9 return pos<k.pos;
10 }
11 }q[200020];
12 int n,m,ans,a[200020],res[200020],cnt[1000020];
13 int main()
14 {
15 scanf("%d",&n);
16 int size=sqrt(n);
17 for(int i=1;i<=n;i++)
18 {
19 scanf("%d",&a[i]);
20 }
21 scanf("%d",&m);
22 for(int i=1;i<=m;i++)
23 {
24 scanf("%d%d",&q[i].l,&q[i].r);
25 q[i].num=i;
26 q[i].pos=(q[i].l-1)/size+1;
27 }
28 sort(q+1,q+m+1);
29 int l=1,r=0;
30 for(int i=1;i<=m;i++)
31 {
32 while(q[i].l<l)
33 {
34 l--;
35 if(cnt[a[l]]==0)
36 {
37 ans++;
38 }
39 cnt[a[l]]++;
40 }
41 while(q[i].r>r)
42 {
43 r++;
44 if(cnt[a[r]]==0)
45 {
46 ans++;
47 }
48 cnt[a[r]]++;
49 }
50 while(q[i].l>l)
51 {
52 cnt[a[l]]--;
53 if(cnt[a[l]]==0)
54 {
55 ans--;
56 }
57 l++;
58 }
59 while(q[i].r<r)
60 {
61 cnt[a[r]]--;
62 if(cnt[a[r]]==0)
63 {
64 ans--;
65 }
66 r--;
67 }
68 res[q[i].num]=ans;
69 }
70 for(int i=1;i<=m;i++)
71 {
72 printf("%d\n",res[i]);
73 }
74 return 0;
75 }
2.洛谷 P2709 小B的询问
又一道莫队裸题。一个元素进入区间的贡献res=(cnt+1)2-cnt2=2*cnt+1。

1 #include<bits/stdc++.h>
2 using namespace std;
3 struct Query
4 {
5 int l,r,num,pos;
6 bool operator < (const Query &k) const
7 {
8 if(pos==k.pos) return r<k.r;
9 return pos<k.pos;
10 }
11 }q[100001];
12 int n,m,k,ans,a[100001],cnt[100001],res[100001];
13 int main()
14 {
15 scanf("%d%d%d",&n,&m,&k);
16 int size=sqrt(n);
17 for(int i=1;i<=n;i++)
18 {
19 scanf("%d",&a[i]);
20 }
21 for(int i=1;i<=m;i++)
22 {
23 scanf("%d%d",&q[i].l,&q[i].r);
24 q[i].num=i;
25 q[i].pos=(q[i].l-1)/size+1;
26 }
27 sort(q+1,q+m+1);
28 int l=1,r=0;
29 for(int i=1;i<=m;i++)
30 {
31 while(q[i].l<l)
32 {
33 l--;
34 cnt[a[l]]++;
35 ans+=2*cnt[a[l]]-1;
36 }
37 while(q[i].r>r)
38 {
39 r++;
40 cnt[a[r]]++;
41 ans+=2*cnt[a[r]]-1;
42 }
43 while(q[i].l>l)
44 {
45 cnt[a[l]]--;
46 ans-=2*cnt[a[l]]+1;
47 l++;
48 }
49 while(q[i].r<r)
50 {
51 cnt[a[r]]--;
52 ans-=2*cnt[a[r]]+1;
53 r--;
54 }
55 res[q[i].num]=ans;
56 }
57 for(int i=1;i<=m;i++)
58 {
59 printf("%d\n",res[i]);
60 }
61 return 0;
62 }
3.洛谷 P1494 小Z的袜子
