top1编程
← 返回题目
题解

逆序对比赛

1 条题解

  • 0
    @ 2026-8-6 0:54:22

    P4651 逆序对比赛(提高)

    解题思路

    第一步,理解"逆序对"的定义。 在序列中找两个位置 i<j,如果 a[i]>a[j],这一对数就叫一个逆序对。注意序列里可能有重复数字,题目要求的是严格大于,也就是两个相等的数不能算作逆序对。比如序列 3,1,2 中,3 和 1、3 和 2 都是逆序对,一共 2 对。

    第二步,想想暴力做法为什么不行。 两重循环枚举所有 i 和 j 自然能数出答案,但如果 n 有几十万甚至上百万,O(n²) 的暴力会完全超时。我们必须想一个更快的办法。

    第三步,请出"归并排序"这位得力助手。 归并排序把序列分成左右两半,各自排好序后再合并。合并时比较左边指针指向的数 a[i] 和右边指针指向的数 a[j]:如果 a[i]<=a[j],说明 a[i] 不会和 a[j] 形成逆序对,直接把 a[i] 放进临时数组;如果 a[i]>a[j],因为左边已经排好序,a[i] 是左边这一段里最小的,所以左边从 a[i] 到 a[mid] 这一整段都比 a[j] 大,它们全部和 a[j] 构成逆序对,一次加上 mid-i+1 个。这样每一对逆序对恰好被统计一次,既不会重复也不会遗漏。

    第四步,注意两个容易踩的坑。 数据里每个数最大 10 亿,用 int 就存得下;但逆序对的数量最多有 n(n-1)/2 对,可能非常大,必须用 long long 来存答案。只要记住这两点,代码就不容易写错。

    第五步,换个角度加深理解。 把序列排成从小到大的顺序,逆序对的个数其实就是"排序过程中需要交换位置的两个数组成的对数"。每一次从右边取出较小的数 a[j] 时,左边还没取走的数都比它大,它们本应排在 a[j] 前面,现在却排在后面,每一对都是一次"位置翻转"。用 answer 把这些数量累加起来,就是最终答案。

    参考代码

    // P4651 逆序对比赛:用归并排序统计满足 a[i]>a[j] 且 i<j 的逆序对个数
    #include <iostream>
    using namespace std;
    
    int num[1000005], temp[1000005];
    long long answer;
    
    // 归并排序,排序过程中顺便统计逆序对
    void mergeSort(int l, int r) {
        if (l >= r) return;
        int mid = (l + r) / 2;
        mergeSort(l, mid);
        mergeSort(mid + 1, r);
        int i = l, j = mid + 1, k = l;
        while (i <= mid && j <= r) {
            if (num[i] <= num[j]) {
                temp[k++] = num[i++];
            } else {
                // num[i]~num[mid] 都比 num[j] 大,都构成逆序对
                answer += mid - i + 1;
                temp[k++] = num[j++];
            }
        }
        while (i <= mid) temp[k++] = num[i++];
        while (j <= r) temp[k++] = num[j++];
        for (i = l; i <= r; i++) num[i] = temp[i];
    }
    
    int main() {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) cin >> num[i];
        answer = 0;
        mergeSort(0, n - 1);
        cout << answer << endl;
        return 0;
    }
    

    复杂度分析

    归并排序的时间复杂度是 O(n log n):每一层合并要遍历全部的 n 个元素,一共有 log2(n) 层。空间复杂度是 O(n),因为需要一个和原数组等长的临时数组 temp。n 即使取到 100 万,log2(100万)≈20,大约 2000 万次操作,也能在一秒内完成。

    • 1