← Все вопросы

Как посчитать число инверсий в массиве через дерево Фенвика за O(n log n)?

Задан 27 месяцев назад297 просмотров2 ответа
9

Классика: посчитать инверсии (пары i<j с a[i]>a[j]). Знаю про merge sort за O(n log n), но хочу через Фенвик — кажется, короче по коду. Как именно, и что делать, если значения большие (до 10^9)?

2 ответа

13
✓ Принятый ответ — помог автору

Идея: идём по массиву слева направо, для каждого a[j] считаем, сколько уже встреченных элементов больше него — это вклад в инверсии. Поддерживаем BIT частот по значениям: уже добавленных элементов всего j, из них ≤ a[j] штук — это query(a[j]), значит больших — j - query(a[j]).

Значения большие → сжимаем координаты (ранги от 1).

long long countInversions(vector<int> a){
    int n = a.size();
    vector<int> srt(a);
    sort(srt.begin(), srt.end());
    srt.erase(unique(srt.begin(), srt.end()), srt.end());
    auto rank = [&](int x){
        return int(lower_bound(srt.begin(), srt.end(), x) - srt.begin()) + 1;
    };
    BIT bit((int)srt.size());
    long long inv = 0;
    for (int j = 0; j < n; ++j) {
        int r = rank(a[j]);
        inv += j - bit.query(r); // среди j добавленных — сколько > a[j]
        bit.update(r, 1);
    }
    return inv;
}

Сложность: сжатие O(n log n), основной цикл n запросов/обновлений по O(log n) — итого O(n log n) время, O(n) память. Результат до n²/2 ≈ 5·10^11 для n=10^6 — обязательно long long.

5

Тонкость про равные элементы: если a[i]==a[j] при i<j, это не инверсия. В коде это учтено тем, что мы считаем j - query(a[j]) = строго больших (равные попадают в query, так как ранг тот же и query включает ). Если случайно посчитать query(rank-1) для «строго меньших» и вычесть иначе — легко словить лишние пары на дубликатах. Проверьте на массиве из одинаковых чисел: инверсий должно быть 0.

Ваш ответ

Войдите, чтобы ответить на вопрос.