莫队_Part one
普通莫队
形式
对于序列上的区间询问问题,如果 \([l, r]\) 的答案可以 \(O(1)\) 扩展到 \([l-1,r],[l+1,r],[l,r-1],[l,r+1]\) 的答案,就能够在 \(O(n\sqrt{n})\) 的时间复杂度内求出所有答案。
概述
将询问离线后,按某种方法排序,按顺序处理每一个询问,每次只在左右端点上进行操作,一步一步移动,暴力从上一个区间转移到下一个区间。
\(code:\)
int n, q;
int a[D], id[D], ans[D];
struct QUE {
int l, r, id;
}que[D];
bool cmp (QUE x, QUE y) {
if (x.l != y.l) return x.l < y.l;
return x.r < y.r;
}
void add (int x) {
// do sth....
}
void del (int x) {
// do sth....
}
int main () {
read (n, q);
for (int i = 1; i <= n; ++ i)
read (a[i]), id[i] = (i - 1) / len + 1;
for (int i = 1; i <= q; ++ i)
read (que[i].l, que[i].r), que[i].id = i;
sort (que + 1, que + 1 + q, cmp);
int l = 1, r = 0;
for (int i = 1; i <= q; ++ i) {
int L = que[i].l, R = que[i].r;
while (L < l) add (-- l);
while (L > l) del (l ++);
while (R < r) del (r --);
while (R > r) add (++ r);
ans[que[i].id] = Ans;
}
for (int i = 1; i <= q; ++ i)
cout << ans[i] << "\n";
return 0;
}
几种排序方法:
- 按端点所在块的编号排序。
if (l != l) return l < x.l;
return r < x.r;
- 奇偶化排序。
看一组数据
1 10
2 200
3 10
4 300
//设块的大小为2
如果使用按端点所在块排序的方法
\(l\) 指针移动了 \(4\) 次,而 \(r\) 指针移动次数为 \(680\) 次,也就是说第一种方法只能保证 \(l\) 指针移动次数尽可能小,而无法保证右端点移动次数。
奇偶化排序
if (a.l / block != b.l / block) return a.l < b.l;
if ((a.l / block) & 1) return a.r < b.r;
return a.r > b.r;
排序后询问会变成这样
2 200
1 10
3 10
4 300
这样移动次数就减小了 \(200\) 次,所以使用奇偶化排序能使程序快 \(30%\) 左右。