逆序对只依赖相对大小,先把值离散化为排名 1..n,再用权值树状数组从左到右「先查前缀和、后单点插入」,每次增量 = 已插入个数 − 前缀和,O(n log n)。
OJ: roj
题目 ID: 1237
难度:普及-
标签:树状数组离散化逆序对python
创建: 2026-09-30 01:08
更新: 2026-10-05 06:20
题目描述
给定 2 6 3 4 5 1 含有 8 个逆序
输入:第一行一个整数
输入样例:
text
6
2 6 3 4 5 1输出样例:
text
8思路
逆序对只依赖元素的相对大小,先把值离散化为排名
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 01:08
# update_at: 2026-09-30 01:17
import sys
def inversions(perm: list[int]) -> int:
"""求排列(互不相同的数)的逆序数:树状数组统计每个元素前面比它大的个数。
逆序对只关心相对大小,所以先把值域压成排名 1..n。从左往右扫,
树状数组的前缀和给出「已经插入的元素里排名不超过当前值」的个数,
用已插入总数减去它就是新产生的逆序对数。
"""
n = len(perm)
rank = {v: i for i, v in enumerate(sorted(perm), 1)} # 值域 1e8 → 排名 1..n
bit = [0] * (n + 1) # 下标即排名,1 表示该排名已出现
ans = 0
for seen, v in enumerate(perm):
i = rank[v]
less = 0 # 已插入元素中排名 ≤ i(即值 ≤ v)的个数
j = i
while j:
less += bit[j]
j -= j & -j
ans += seen - less # 前面 seen 个里比 v 大的都是逆序
j = i
while j <= n:
bit[j] += 1
j += j & -j
return ans
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data) # 排列长度
perm = [next(data) for _ in range(n)]
print(inversions(perm))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 06:20
* update_at: 2026-10-05 06:20
*/
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
// a[i]:原序列的值;rk[i]:离散化后的排名(第几小)
int a[MAXN];
int rk[MAXN];
// 树状数组:下标是排名,存"该排名是否已出现过"的计数
int bit[MAXN];
int n;
// 树状数组单点加 1
void bit_add(int i) {
for (; i <= n; i += i & (-i))
bit[i] += 1;
}
// 树状数组前缀和:排名 <= i 的已插入元素个数
int bit_sum(int i) {
int s = 0;
for (; i > 0; i -= i & (-i))
s += bit[i];
return s;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
// 离散化:逆序对只依赖相对大小,把值压成排名 1..n
for (int i = 1; i <= n; ++i)
rk[i] = a[i];
sort(rk + 1, rk + n + 1);
ll ans = 0; // 完全降序时答案可达 n(n-1)/2,要用 long long
int seen = 0; // 已经插入树状数组的元素个数
for (int i = 1; i <= n; ++i) {
// 当前值的排名:第一个大于等于 a[i] 的位置(元素互不相同,即第几小)
int pos = lower_bound(rk + 1, rk + n + 1, a[i]) - rk;
// 前面 seen 个数中比 a[i] 大的个数 = seen - (排名 <= pos 的个数)
ans += seen - bit_sum(pos);
bit_add(pos);
seen++;
}
printf("%lld\n", ans);
return 0;
}