AcWing 788. 逆序对的数量

前置知识 归并排序

不会的点这里QwQ

逆序对算法

定义

逆序对的定义如下:对于数列的第 \(i\) 个和第 \(j\) 个元素,如果满 \(i \lt j\)\(a_i \gt a_j\),则其为一个逆序对;否则不是。

分析

用分治算法解决。

我们将序列从中间分开,将逆序对分成三类:

  • 两个元素都在左边;
  • 两个元素都在右边;
  • 两个元素一个在左一个在右。

我们可以推出大概流程:

  • 算左边的;
  • 算右边的;
  • 算一左一右的;
  • 相加。

如何计算

我们发现一个性质:当数组分为左右两部分时,其中一个部分中的数字位置进行了交换并不会影响另外一部分与该部分之间产生的逆序对数(也就是“一左一右”的情况)。 根据归并排序流程,发现可以用归并排序排序。

有什么好处?

如果右侧指针指向的数字小于左侧指针指向的数字,那么说明左侧指针所指向的数字以及该序列之后的数字均大于右侧指针所指向的数字,所及将这些数字全部记录,ans += mid - i + 1即可。

算法时间复杂度

与归并排序相同,复杂度 \(O(n \log n)\)

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;

const int N = 100010;

long long n, a[N], ans, r[N];

void nxd(long long s, long long t)
{
    long long mid = (s + t) / 2, i = s, j = mid + 1, k = s;
    if (s == t) return;
    nxd(s, mid);
    nxd(mid + 1, t);
    while (i <= mid && j <= t)
    {
        if (a[i] <= a[j]) r[k] = a[i], i ++ , k ++ ;
        else r[k] = a[j], j ++ , k ++ , ans += mid - i + 1; // ans累加答案,其他部分与归并排序相同,不做解释 
    }
    while (i <= mid) r[k] = a[i], i ++ , k ++ ;
    while (j <= t) r[k] = a[j], j ++ , k ++ ;
    for (int i = s; i <= t; i ++ ) a[i] = r[i];
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    nxd(1, n);
    cout << ans;
    return 0;
}
posted @ 2022-06-28 20:57  FXT1110011010OI  阅读(40)  评论(0编辑  收藏  举报