莫队算法(复杂度 n*sqrt(n))
听说是区间处理的神器,这个算法的本质是将区间分为0-sqrt(n), sqrt(n)-2sqrt(n),......,通过这个square root decompostion可以降低复杂度,使得区间查询的复杂度变为n*sqrt(n).
为什么这样分解有用呢?因为,我们发现,有些区间查询的结果可以利用上一次查询的结果来减少运算,这里有点DP的意味。以区间rsq为例(其实这个例子好像不太恰当,rsq问题用线段树的复杂度是 nlogn)。例子摘自geeksforgeeks。
算法步骤:
1. 首先对查询的区间排序,区间是[L,R],那么我们将L的 0-sqrt(n) , sqrt(n) - 2sqrt(n),分成不同的block,在同一个block中,我们将R从小到大排序。
2.第一次查询时,我们得到一个初始区间的rsq结果。
3.遍历之后的查询,定义变量 (lastL , lastR ,分别为上一次查询的[L,R] , curL,curR,为本次查询的[L,R] ,sum为上次查询的结果) 若curL< lastL,那么sum就要加上多出来的区间,若curL>lastL,sum就要减去多出来的区间。若curL==lastL,sum不变。后面也要比较curR和lastR,curR<lastR,那么sum就要减去多出来的区间,若curR>lastR,那么sum就要加上多出来的区间,curR==lastR,sum不变。
代码:
#include <bits/stdc++.h>
using namespace std;
int block;
bool cmp(pair<int, int> &fir, pair<int, int> &las) {
if (fir.first / block != las.first / block)return fir.first / block<las.first / block;
return fir.second<las.second;
}
void queryResults(int a[], int n, vector<pair<int, int> > q) {
block = sqrt(n);
sort(q.begin(), q.end(), cmp);
int lastL = 0; int lastR = 0;
int sum = a[0];
for (int i = 0; i<(int)q.size(); i++){
int CurL = q[i].first; int CurR = q[i].second;
int tmpl = CurL; int tmpr = CurR;
while (CurL<lastL) {
sum += a[CurL];
CurL++;
}
CurL = tmpl;
while (CurL>lastL ) {
CurL--;
if(CurL >=0)
sum -= a[CurL];
}
lastL = tmpl;
while (CurR<lastR) {
CurR += 1;
sum -= a[CurR];
}
CurR = tmpr;
while (CurR>lastR) {
sum += a[CurR];
CurR--;
}
lastR = tmpr;
cout << "sum of " << "L [" << tmpl << " " << tmpr << " ] R " << sum << endl;
}
}
int main() {
int a[] = { 1, 1, 2, 1, 3, 4, 5, 2, 8 };
int n = sizeof(a) / sizeof(a[0]);
vector<pair<int, int>> q;
q.push_back(make_pair(2, 2));
q.push_back(make_pair(1, 4));
q.push_back(make_pair(1, 3));
q.push_back(make_pair(1, 5));
q.push_back(make_pair(0, 5));
q.push_back(make_pair(0, 4));
q.push_back(make_pair(2, 4));
queryResults(a, n, q);
return 0;
}