求數組逆序對數量的經典問題。n ≤ 500,000,O(n²) 暴力不可行,需 O(n log n) 算法。本文講解樹狀數組(Fenwick Tree)配合離散化的解法。
逆序對定義:i < j 且 a[i] > a[j]。遍歷數組,對於每個 a[i],查詢「已處理元素中大於 a[i] 的個數」即為以 i 為右端點的逆序對數。累加即得答案。
樹狀數組下標需為連續正整數。原數組值可能很大、稀疏、含負數。離散化步驟:
例:[9, 1, 0, 5, 4] → 排序去重 [0,1,4,5,9] → 映射 [5, 2, 1, 4, 3]
update(pos, 1):在位置 pos 加 1,表示出現了一個值query(pos):查詢前綴和 [1..pos],即「≤ 當前值的已處理元素個數」r,已處理總數 i-1,則大於它的個數 = (i-1) - query(r)#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct BIT {
int n; vector t;
BIT(int n=0): n(n), t(n+1, 0) {}
void add(int x, int v=1) { for(; x<=n; x+=x&-x) t[x] += v; }
int sum(int x) { int r=0; for(; x>0; x-=x&-x) r+=t[x]; return r; }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
while (cin >> n && n) {
vector a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
// 離散化
vector vals = a;
sort(vals.begin(), vals.end());
vals.erase(unique(vals.begin(), vals.end()), vals.end());
for (int& x : a) x = lower_bound(vals.begin(), vals.end(), x) - vals.begin() + 1;
// 統計逆序對
BIT bit(vals.size());
ll ans = 0;
for (int i = 0; i < n; ++i) {
ans += i - bit.sum(a[i]); // i = 已處理個數
bit.add(a[i]);
}
cout << ans << '\n';
}
return 0;
}
O(n log n)(排序 + n 次 BIT 操作)O(n)歸並排序過程中合併步驟天然統計逆序對,代碼更短、常數更小,但 BIT 靈活性更強(可支持區間查詢、修改等動態操作)。面試/競賽中兩者皆可,掌握 BIT 更具通用性。
[1..m]ans += processed_count - query(rank)n*(n-1)/2 ≈ 1.25e11,必須用 64 位整型(long long/int64_t)此題是「離散化 + 樹狀數組」的標準模板題,同技巧廣泛應用於區間第 K 小、動態逆序對、矩形面積並等問題。