莫队_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%\) 左右。

posted @ 2022-08-05 08:39  zcxxxxx  阅读(24)  评论(0编辑  收藏  举报