莫队学习笔记
莫队学习笔记
什么是莫队?
莫队是一种离线数据结构类的算法。
我们先考虑一类问题:
在一个序列上面查询区间信息。那么对于这样一类问题,首先想到各种高级的数据结构。
但莫队也可以很好的实现它。
普通莫队
莫队的思想是:如果我们已经了\([l,r]\)的区间信息,我们便可以通过指针转移暴力的快速转移到其他区间。这样,我们每一次都只需要通过前一次的区间信息得出下一次的区间。
但是有一个大问题,要是毒瘤出题人要你这个区间在最左边和最右边来回左右横跳,那就完了,时间复杂度被卡到了\(O(n^2)\)
怎么办?
莫队的精髓就在于离线操作,把所有的询问按分块的思想来调整,排序。我们可以把它们分成\(\sqrt{n}\)块来处理排序,这样子我们就可以减少区间跳动次数,来保证时间效率。
所以对于两次询问之间,我们先判定它们是不是属于同一个分块,如果是就以\(l\)作为关键字排序,否则以\(r\)为关键字排序。
cmp代码:
bool cmp(int i, int j){
if(l[i]/k != l[j]/k) return l[i]<l[j];
return r[i] < r[j];
}
来看一个例题:
洛谷P1494 [国家集训队]小Z的袜子
https://www.luogu.com.cn/problem/P1494
这道题其实就是让你求求一个区间内每种颜色数目的平方和。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int A = 50005;
int l[A], r[A], id[A], a[A];
int n, m, k;
int now;//区间和
int ans[A];
int cnt[A];
bool cmp(int i, int j){
if(l[i]/k != l[j]/k) return l[i]<l[j];
return r[i] < r[j];
}
void add(int x){
now -= cnt[x] * (cnt[x] - 1) / 2;
++cnt[x];
now += cnt[x] * (cnt[x] - 1) / 2;
}
void del(int x){
now -= cnt[x] * (cnt[x] - 1) / 2;
--cnt[x];
now += cnt[x] * (cnt[x] - 1) / 2;
}
signed main(){
cin>>n>>m;
for(int i = 1; i <= n; i++) cin>>a[i];
for(int i = 1; i <= m; i++){
cin>>l[i]>>r[i];
id[i] = i;
}
k = sqrt(n);
sort(id+1, id+m+1, cmp);
int s = 1, t = 0;//当前区间
for(int j = 1; j <= m; j++){
int i = id[j];
while(s > l[i]) add(a[--s]);
while(t < r[i]) add(a[++t]);
while(s < l[i]) del(a[s++]);
while(t > r[i]) del(a[t--]);
ans[i] = now;
}
for(int i = 1; i <= m; i++){
int tot = (r[i] - l[i] + 1) * (r[i] - l[i]) / 2;
if(r[i] == l[i] or tot == 0){
puts("0/1");
continue;
}
int g = __gcd(ans[i], tot);
int aa = ans[i]/g, bb = tot/g;
cout<<ans[i]/g<<'/'<<tot/g<<endl;
}
return 0;
}