788-逆序对的数量
题目描述
关系
内容
给定一个长度为
逆序对的定义如下:对于数列的第
输入格式
第一行包含整数
第二行包含
输出格式
输出一个整数,表示逆序对的个数。
数据范围
数列中的元素的取值范围
输入样例:
6
2 3 4 5 6 1
输出样例:
5
问题分析
最初思路
思路分析
先假定归并排序把序列进行排序并返回逆序对的数量。
然后当两个数在左边或者右边的时候就是直接调用归并排序返回。
当出现一个在左,一个在右的时候,就是第三种情况,利用多路归并算法进行求解。
而前面两种由于递归的性质最后都会变成第三种情况。
为什么排序之后不会改变逆序对的个数?
利用黑盒的思路来理解。首先,我们假设归并分治的过程,能够处理过左右两边的逆序对的个数。现在的关键就是合并左右两边的结果。
逆序对有三种情况:
- 逆序对在左半边或者右半边,这已经由子过程处理完毕了;
- 逆序对位于左右两半边;
对于第二种情况,我们我们要想办法把合并起来,得出这种情况的个数。
我们可以发现一个重要的性质,就是无论左半边(右半边)的元素怎么调换位置,都不会影响第二种情况的逆序对个数。所以归并分治的过程就得以成立。我们就可以对左半边和右半边的元素进行排序。
执行流程设计
总结
代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
int a[N], t[N];
ll merge_sort(int a[], int l, int r) {
if (l >= r) return 0;
int mid = l + r >> 1;
ll res = merge_sort(a, l, mid) + merge_sort(a, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r) {
if (a[i] <= a[j]) t[k++] = a[i++];
else {
t[k++] = a[j++];
res += mid - i + 1;
}
}
while (i <= mid) t[k++] = a[i++];
while (j <= r) t[k++] = a[j++];
for (int i = l, j = 0; i <= r; i++, j++) a[i] = t[j];
return res;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
cout << merge_sort(a, 0, n - 1) << endl;
return 0;
}