题目地址:
https://www.acwing.com/problem/content/description/790/
给定一个长 n n n的数列,计算其逆序对数量。
输入格式:
第一行包含整数
n
n
n,表示数列的长度。第二行包含
n
n
n个整数,表示整个数列。
输出格式:
输出一个整数,表示逆序对的个数。
数据范围:
1
≤
n
≤
100000
1\le n\le 100000
1≤n≤100000
思路是归并排序(分治)。先累加左右两边的逆序对数量,然后做二路归并的时候,顺便计算跨越两边的逆序对数量。由于左右两半边已经排好序了,计算跨越两边的逆序对数量变得很简单。当右半边的指针 i i i指向的数 y y y小于左半边指针 j j j指向的数 x x x的时候,就可以累加以 y y y为第二个数的逆序对数列,就是 m − i + 1 m-i+1 m−i+1, m m m是左半边的最后一个数的下标。代码如下:
#include <iostream>
using namespace std;
using ll = long long;
const int N = 100010;
int n;
int a[N], tmp[N];
ll merge_sort(int l, int r) {
if (l >= r) return 0;
int m = l + (r - l >> 1);
// 递归求解左右两半边内部的逆序对个数
ll res = merge_sort(l, m) + merge_sort(m + 1, r);
// 利用双指针做二路归并,并且将跨中线的逆序对个数累加到res里
int i = l, j = m + 1, idx = l;
while (i <= m && j <= r) {
if (a[i] > a[j]) {
res += m - i + 1;
tmp[idx++] = a[j++];
} else
tmp[idx++] = a[i++];
}
while (i <= m) tmp[idx++] = a[i++];
while (j <= r) tmp[idx++] = a[j++];
for (i = l; i <= r; i++) a[i] = tmp[i];
return res;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
cout << merge_sort(0, n - 1) << endl;
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)。