Как посчитать число инверсий в массиве через дерево Фенвика за O(n log n)?
Классика: посчитать инверсии (пары i<j с a[i]>a[j]). Знаю про merge sort за O(n log n), но хочу через Фенвик — кажется, короче по коду. Как именно, и что делать, если значения большие (до 10^9)?
2 ответа
Идея: идём по массиву слева направо, для каждого 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.
Тонкость про равные элементы: если a[i]==a[j] при i<j, это не инверсия. В коде это учтено тем, что мы считаем j - query(a[j]) = строго больших (равные попадают в query, так как ранг тот же и query включает ≤). Если случайно посчитать query(rank-1) для «строго меньших» и вычесть иначе — легко словить лишние пары на дубликатах. Проверьте на массиве из одинаковых чисел: инверсий должно быть 0.