题目描述
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。比如一个序列为4 5 1 3 2, 那么这个序列的逆序数为7,逆序对分别为(4, 1), (4, 3), (4, 2), (5, 1), (5, 3), (5, 2),(3, 2)。
输入描述:
第一行有一个整数n(1 <= n <= 100000), 然后第二行跟着n个整数,对于第i个数a[i],(0 <= a[i] <= 100000)。
输出描述:
输出这个序列中的逆序数
输入
5
4 5 1 3 2
输出
7
题目分析
这是一个经典的归并排序的模板题,也是一个树状数组的模板题,但是我不会树状数组!!!,那我们现在来讲一下什么是归并排序。
- 首先就是递归,递归将一个数组分为两部分,然后再次递归将分完之后的每一部分分为两部分,然后不断地递归,直到每部分只剩下一个元素为止。
- 然后再从这无数个数据在逐渐的合并,用二路归并,每次归并我们都计算一下逆序对的数量然后累加,但是每次归并的数组都是临时数组,会在后面的归并中覆盖,二路归并前提是要是两个有序的数组,所以这就是为什么分到最后每一部分都是一个元素,因为一个元素再怎么都是有序的,然后从下往上去二路归并。
- 那么如何求逆序对的数量呢,在二路归并的时候,如果后面的数跑到前面去了,我们就计算中间路过了多少个数,那么它前面就有多少个数比它大。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005;
ll a[maxn], b[maxn], cnt;
void merge(ll l, ll mid, ll r)
{
ll i = l, j = mid + 1, t = 1;
while (i <= mid && j <= r)
{
if (a[i] > a[j])
{
b[t++] = a[j++];
cnt += mid - i + 1; //记录逆序对数量
}
else
b[t++] = a[i++];
}
//一个子序列中的数都处理完了,另一个还没有,把剩下的直接复制过来
while (i <= mid)
b[t++] = a[i++];
while (j <= r)
b[t++] = a[j++];
for (int i = r; i >= l; i--)
a[i] = b[--t]; //把排好序的b数组复制回a数组
}
void mergesort(ll l, ll r)
{
if (l == r)
return;
int mid = (l + r) >> 1; //平均分为两个子序列
mergesort(l, mid);
mergesort(mid + 1, r);
merge(l, mid, r); //合并
}
int main()
{
ll n, k;
while (~scanf("%lld", &n))
{
cnt = 0;
for (ll i = 1; i <= n; ++i)
scanf("%lld", &a[i]);
mergesort(1, n); //归并排序
printf("%lld\n", cnt);
}
return 0;
}
流年似水,有些事一下子过去了,有些事很久也过不去。有些伤痕可以慢慢被修复,而有些伤痕,却如同钉入木桩中的钉子一般,即使拔出,也烙下深深的印痕,无法忘却 |
---|