NC15163 逆序数

题目

题目描述

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。比如一个序列为 \(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)\)

输出描述

输出这个序列中的逆序数

示例1

输入

5
4 5 1 3 2

输出

7

题解

知识点:递归,排序。

众所周知,排序可以理解为把一个具有逆序数的序列变换为逆序数为零的序列。而归并排序每次交换元素,只会导致逆序数减少,而且可以非常容易的计算。

归并排序先把序列对半分,直到只有一个元素(可视为排序好的)开始回溯进行排序。而将两个排序好的序列归并是很容易的,只要用两个指针 \(i,j\) 分别指向两个数组的头部开始遍历,再创建一个临时数组,依次哪个小就放哪个进临时数组,最后覆盖回去即可。在这个过程中,各自序列的元素的相对位置不会改变,而左序列元素和右序列各个元素的相对位置可能会发生改变。每次遇到一个左序列元素大于右序列元素,则将右序列元素提前,仅在这个过程会仅减少逆序数,值为 \(mid-i+1\) ,即左序列剩余元素个数。将每次归并减少的逆序数累加,就是最终答案。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

long long cnt = 0;
int a[100007], b[100007];
void merge_sort(int l, int r) {
    if (l == r) return;
    int mid = l + r >> 1;
    merge_sort(l, mid);
    merge_sort(mid + 1, r);
    int i = l, j = mid + 1, k = l;
    while (i <= mid && j <= r) {
        if (a[i] <= a[j]) b[k++] = a[i++];
        else b[k++] = a[j++], cnt += mid - i + 1;
    }
    while (i <= mid) b[k++] = a[i++];
    while (j <= r) b[k++] = a[j++];
    for (int i = l;i <= r;i++) a[i] = b[i];
}


int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 0;i < n;i++) cin >> a[i];
    merge_sort(0, n - 1);
    cout << cnt << '\n';
    return 0;
}
posted @ 2022-06-22 23:32  空白菌  阅读(32)  评论(0编辑  收藏  举报