题目链接:http://www.jxsfczx.cn:888/problem/35
时间:1 秒 空间:512 MB

题目描述

给定一个序列a1,a2,…,an,如果存在 i < j 并且ai > aj,那么我们称之为逆序对,求逆序对的数目。
数据范围:N<=10^5。Ai<=10^5。时间限制为1s。

输入描述

第一行为n,表示序列长度,接下来的n个数表示序列中的第i个数。

输出描述

所有逆序对总数.

输入

4
3 2 3 2

输出

3

解题思路

求逆序数可以通过管理两个指针,两次扫描数组,暴力法求出,显然时间复杂度是O(n^2).那么有更快的办法吗?答案是肯定的。我们可以利用归并排序法,稍做改进即可.

算法:归并排序是分治法(分而治之)的一种典型应用,应用递归的思想,自顶向下思考:先假定MergeSort()可以将一个乱序的数组排好序,因此就可以开始"分"(将一个数组平均分成两部分),再"治"(分别对前后部分调用MergeSort()使它们有序),最后再写一个合并子函数Merge(),它可以将两个有序的数组合并,Merge()实现起来比较容易.只需要管理两个指针,分别指向两个子数组的开头,开辟新内存保存中间结果,遍历完两个数组就可以完成,时间复杂度是O(n).

求逆序数对时,在Merge()中,合并两个已经有序的数组A、B.因为A、B有序,所以A,B各自的逆序数是0,故A、B的逆序数等于A,B之间的逆序数.

#include <bits/stdc++.h>
using namespace std;
long long ans = 0;
void Merge(int A[], int L, int Mid, int R) {
    int *Temp = (int *)malloc((R - L + 1) * sizeof(int));
    int p = L, q = Mid, k = 0;
    while (p <= Mid - 1 && q <= R) {
        if (A[p] <= A[q])
            Temp[k++] = A[p++];
        else {
            Temp[k++] = A[q++];
            ans += Mid - p;//只是比归并排序多了这个.右边小,则左边剩下的都是这个数的逆序对
        }
    }
    while (p <= Mid - 1)
        Temp[k++] = A[p++];
    while (q <= R)
        Temp[k++] = A[q++];
    for (int i = 0; i < k; i++)
        A[i + L] = Temp[i];
}
void MSort(int A[], int L, int RightEnd) {
    if (L < RightEnd) {
        int mid = (L + RightEnd) / 2;
        MSort(A, L, mid);
        MSort(A, mid + 1, RightEnd);
        Merge(A, L, mid + 1, RightEnd);
    }
}
int main() {
    int n, A[100005];
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> A[i];
    MSort(A, 0, n - 1);
    cout << ans << endl;
    return 0;
}