旋转一次即交换相邻车厢,最少次数恰为逆序对数;离散化后用权值树状数组从左到右先查前缀和、后单点插入,O(n log n) 求出。
OJ: roj
题目 ID: 1310
难度:普及-
标签:树状数组离散化逆序对python
创建: 2026-09-30 04:17
更新: 2026-10-07 13:50
题目描述
旧式火车站有一座桥,旋转 180 度可交换相邻两节车厢。给定车厢总数
输入:第一行是
样例输入:
text
4
4 3 2 1样例输出:
text
6思路
每次相邻交换使逆序数变化 已出现总数 − 前缀和 得到左边比它大的元素数,累加后把当前元素插入。
参考代码
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 04:18
# update_at: 2026-09-30 04:18
import sys
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
a = [next(data) for _ in range(n)]
# 离散化:车厢号两两不同,排名即在排序后序列中的位置,压到 1..n 方便树状数组下标
rank = {v: i for i, v in enumerate(sorted(a), 1)}
tree = [0] * (n + 1) # 树状数组:维护"已出现元素"按排名的计数
inversions = 0 # 逆序对总数 = 最少旋转次数
seen = 0 # 已扫过的元素个数
for x in a:
i = rank[x]
# 前缀和:排名 <= i 的已出现元素个数
prefix = 0
j = i
while j:
prefix += tree[j]
j -= j & -j
# 比 x 大的已出现元素 = 它们与 x 各构成一个逆序对
inversions += seen - prefix
j = i # 把 x 计入树状数组
while j <= n:
tree[j] += 1
j += j & -j
seen += 1
print(inversions)
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 08:58
* update_at: 2026-10-05 08:58
*/
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 10005; // 车厢数上限
int n;
int a[MAXN]; // 原始车厢顺序
int sorted_a[MAXN]; // 用于离散化的副本
int tree[MAXN]; // 权值树状数组:维护已插入元素按排名的计数
// 树状数组前缀和查询:排名 <= x 的已插入元素个数
ll query(int x) {
ll s = 0;
for (int i = x; i > 0; i -= i & -i) {
s += tree[i];
}
return s;
}
// 树状数组单点增加:排名为 x 的位置加 1
void add(int x) {
for (int i = x; i <= n; i += i & -i) {
tree[i] += 1;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sorted_a[i] = a[i];
}
// 离散化:把车厢号映射为 1..n 的排名
sort(sorted_a + 1, sorted_a + n + 1);
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(sorted_a + 1, sorted_a + n + 1, a[i]) - sorted_a;
}
ll inversions = 0; // 逆序对总数 = 最少旋转次数
for (int i = 1; i <= n; i++) {
// 已插入元素总数 - 比 a[i] 小或相等的已插入元素数 = 左边比 a[i] 大的元素数
inversions += (ll)(i - 1) - query(a[i]);
add(a[i]); // 把当前元素插入树状数组
}
printf("%lld\n", inversions);
return 0;
}