跳转至

莫队算法

前置知识

分块思想

目标

普通莫队算法

What

对于序列上的区间询问问题,离线后排序,顺序处理每个询问,暴力从上一个区间的答案,转移到下一个区间的答案。[l, r] 的答案,可以 �(1) 的时间,扩展到 [l - 1, r], [l + 1, r], [l, r + 1], [l, r - 1]的答案,那么就可以用莫队算法,在 �(��) 的时间复杂度求解。

例题,SPOJ-DQUERY

题意,m次询问,每次询问,输出[l, r]区间内,有多少个不同的数字

#include <bits/stdc++.h>

using namespace std;

const int N = 30010, M = 2e5 + 10, SIZE = 1e6 + 10;

int a[N], n, m;
int B; // Block-size
int l = 1, r = 0, now; // the initial number, l = 1, r = 0, like queue, like slide-window
int ans[M]; // record offline answer
int cnt[SIZE]; 

struct mo {
    int l, r, id;

    bool operator< (const mo &W) const{
        if (l / B != W.l / B) return l < W.l;

        // Constant optimization
        // in odd block, the r point, left -> right
        // in even block, the r point, left <- right
        if ((l / B) & 1) return r < W.r;
        return r > W.r;
    }
}q[M];

void add(int p) {
    if (cnt[a[p]] == 0) now++;
    cnt[a[p]]++;
}

void del(int p) {
    cnt[a[p]]--;
    if (cnt[a[p]] == 0) now--;
}

int main() {
    cin >> n;
    B = sqrt(n);
    for (int i = 1; i <= n; i++) cin >> a[i];

    cin >> m;
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &q[i].l, &q[i].r);
        q[i].id = i;
    }

    sort(q, q + m);

    for (int i = 0; i < m; i++) {
        while (l > q[i].l) add(--l);
        while (r < q[i].r) add(++r);
        while (l < q[i].l) del(l++);
        while (r > q[i].r) del(r--);
        ans[q[i].id] = now;
    }

    for (int i = 0; i < m; i++) printf("%d\n", ans[i]);

    return 0;
}

题单

SPOJ-DQUERY [P1972 SDOI2009] HH的项链 P2709 小B的询问 CF617E XOR and Favorite Number P3709 大爷的字符串题

总结

访问莫队本人的知乎,点击:莫涛 Orz... 观摩学习真正的大佬

参考

普通莫队算法 - OI Wiki Pecco:算法学习笔记(24): 莫队 莫队算法--从入门到黑题 - WAMonster - 博客园 浅谈一类“暴力”在OI中的应用--莫队 - DPairの博客 - 洛谷博客