跳转至

莫队

简介

莫队算法可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。

形式

假设 \(n=m\),那么对于序列上的区间询问问题,如果从 \([l,r]\) 的答案能够 \(O(1)\) 扩展到 \([l-1,r],[l+1,r],[l,r+1],[l,r-1]\)(即与 \([l,r]\) 相邻的区间)的答案,那么可以在 \(O(n\sqrt{n})\) 的复杂度内求出所有询问的答案。

解释

离线后排序,顺序处理每个询问,暴力从上一个区间的答案转移到下一个区间答案(一步一步移动即可)。

排序方法

对于区间 \([l,r]\) , 以 \(l\) 所在块的编号为第一关键字,\(r\) 为第二关键字从小到大排序。

复杂度证明可看下面回滚莫队的。

例题

P2709 小B的询问

小B 有一个长为 \(n\) 的整数序列 \(a\),值域为 \([1,k]\)。 他一共有 \(m\) 个询问,每个询问给定一个区间 \([l,r]\),求:

\[\sum\limits_{i=1}^k c_i^2\]

其中 \(c_i\) 表示数字 \(i\)\([l,r]\) 中的出现次数。

对于 \(100\%\) 的数据,\(1\le n,m,k \le 5\times 10^4\)

先按上述的方法排序,对于每次对 \(c_i\) 的更新,贡献为 \(\pm 2c_i+1\),直接维护即可。

实现
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>
using namespace std;
#define N 50005
int n, m, k, a[N], cnt[N], ans[N];
struct node
{
    int l, r, id;
} q[N];
int cmp(node x, node y)
{
    if (x.l / 250 == y.l / 250)
        return x.r < y.r;
    return x.l / 250 < y.l / 250;
}
int main()
{
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d", &q[i].l, &q[i].r);
        q[i].id = i;
    }
    sort(q + 1, q + 1 + m, cmp);
    int l = 1, r = 0, now = 0;
    for (int i = 1; i <= m; i++)
    {
        for (int j = l; j < q[i].l; j++)
        {
            now += cnt[a[j]] * -2 + 1;
            cnt[a[j]]--;
        }
        for (int j = q[i].l; j < l; j++)
        {
            now += cnt[a[j]] * 2 + 1;
            cnt[a[j]]++;
        }
        for (int j = q[i].r + 1; j <= r; j++)
        {
            now += cnt[a[j]] * -2 + 1;
            cnt[a[j]]--;
        }
        for (int j = r + 1; j <= q[i].r; j++)
        {
            now += cnt[a[j]] * 2 + 1;
            cnt[a[j]]++;
        }
        l = q[i].l;
        r = q[i].r;
        ans[q[i].id] = now;
    }
    for (int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
    return 0;
}