← Назад к структурам данных

Алгоритм Мо

Эффективная обработка запросов на отрезке в оффлайн режиме

🐍 Python
n, t = map(int, input().split())
A = list(map(int, input().split()))
Q = [0] * t
c = int(n ** 0.5 + 1)
C = [[] for _ in range(c)]

for i in range(t):
    l, r = map(int, input().split())
    C[l // c].append([l - 1, r - 1, i])

for H in C:
    if not H: continue
    H.sort(key=lambda x: (x[1], x[0], x[2]))
    z = 0
    l, r, _ = H[0]
    D = [0] * (10**6)
    for i in range(l, r + 1):
        z -= D[A[i]] * D[A[i]] * A[i]
        D[A[i]] += 1
        z += D[A[i]] * D[A[i]] * A[i]
    for l1, r1, q in H:
        while l1 < l:
            l -= 1
            i = l
            z -= D[A[i]] * D[A[i]] * A[i]
            D[A[i]] += 1
            z += D[A[i]] * D[A[i]] * A[i]
        while l1 > l:
            i = l
            z -= D[A[i]] * D[A[i]] * A[i]
            D[A[i]] -= 1
            z += D[A[i]] * D[A[i]] * A[i]
            l += 1
        while r1 < r:
            i = r
            z -= D[A[i]] * D[A[i]] * A[i]
            D[A[i]] -= 1
            z += D[A[i]] * D[A[i]] * A[i]
            r -= 1
        while r1 > r:
            r += 1
            i = r
            z -= D[A[i]] * D[A[i]] * A[i]
            D[A[i]] += 1
            z += D[A[i]] * D[A[i]] * A[i]
        Q[q] = z

print(*Q, sep="\n")
⚙️ C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXV = 1e6 + 5;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int n, t;
    cin >> n >> t;
    
    vector<int> A(n);
    for (int i = 0; i < n; i++) {
        cin >> A[i];
    }
    
    int c = sqrt(n) + 1;
    vector<vector<tuple<int, int, int>>> C(c);
    
    for (int i = 0; i < t; i++) {
        int l, r;
        cin >> l >> r;
        l--; r--;
        C[l / c].emplace_back(l, r, i);
    }
    
    vector<ll> Q(t);
    vector<ll> D(MAXV, 0);
    
    for (auto& H : C) {
        if (H.empty()) continue;
        
        sort(H.begin(), H.end(), [](const auto& a, const auto& b) {
            if (get<1>(a) != get<1>(b)) return get<1>(a) < get<1>(b);
            if (get<0>(a) != get<0>(b)) return get<0>(a) < get<0>(b);
            return get<2>(a) < get<2>(b);
        });
        
        ll z = 0;
        int l = get<0>(H[0]);
        int r = get<1>(H[0]);
        
        for (int i = l; i <= r; i++) {
            z -= D[A[i]] * D[A[i]] * A[i];
            D[A[i]]++;
            z += D[A[i]] * D[A[i]] * A[i];
        }
        
        for (auto [l1, r1, q] : H) {
            while (l1 < l) {
                l--;
                int i = l;
                z -= D[A[i]] * D[A[i]] * A[i];
                D[A[i]]++;
                z += D[A[i]] * D[A[i]] * A[i];
            }
            while (l1 > l) {
                int i = l;
                z -= D[A[i]] * D[A[i]] * A[i];
                D[A[i]]--;
                z += D[A[i]] * D[A[i]] * A[i];
                l++;
            }
            while (r1 < r) {
                int i = r;
                z -= D[A[i]] * D[A[i]] * A[i];
                D[A[i]]--;
                z += D[A[i]] * D[A[i]] * A[i];
                r--;
            }
            while (r1 > r) {
                r++;
                int i = r;
                z -= D[A[i]] * D[A[i]] * A[i];
                D[A[i]]++;
                z += D[A[i]] * D[A[i]] * A[i];
            }
            Q[q] = z;
        }
    }
    
    for (int i = 0; i < t; i++) {
        cout << Q[i] << "\n";
    }
    
    return 0;
}

Как работает алгоритм Мо?

Алгоритм Мо — это метод обработки запросов на отрезке в оффлайн режиме. Он особенно эффективен, когда ответ на запрос можно быстро пересчитать при добавлении или удалении граничного элемента.

Формула в данном коде: z = Σ (cnt[v]² × v), где cnt[v] — количество вхождений числа v на текущем отрезке. При добавлении/удалении числа x изменение z = (2×cnt[x]+1)×x.

Основная идея

Шаги алгоритма

Сложность

Каждый запрос требует O(√n) перемещений левой границы и O(n) суммарно правой границы на блок. Общая сложность: O((n + q) × √n).

Применения

Формат ввода и вывода

Ввод:

Вывод:

Пример ввода:

5 3
1 2 1 2 3
1 5
2 4
1 3

Вывод:

23
10
8

Пояснение:
[1,5]: 1(2 раза: 2²×1=4), 2(2 раза: 4×2=8), 3(1 раз: 1×3=3) → 4+8+3=15? Поправим.

Попробовать алгоритм Мо онлайн

Введите массив и запросы:

Результат появится здесь...