题目描述
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。比如一个序列为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;
}
流年似水,有些事一下子过去了,有些事很久也过不去。有些伤痕可以慢慢被修复,而有些伤痕,却如同钉入木桩中的钉子一般,即使拔出,也烙下深深的印痕,无法忘却