求排列的逆序数(分治)

考虑1,2,…,n (n <= 100000)的排列i1,i2,…,in,如果其中存在j,k,满足 j < k 且 ij > ik, 那么就称(ij,ik)是这个排列的一个逆序。
一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个 逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。
现给定1,2,…,n的一个排列,求它的逆序数。

笨办法:O(n2)

分治O(nlogn):
1) 将数组分成两半,分别求出左半边的逆序数和右半边的逆序数
2) 再算有多少逆序是由左半边取一个数和右半边取一个数构成(要求O(n)实 现)

由归并排序改进得到,加上计算逆序的步骤

MergeSortAndCount: 归并排序并计算逆序数

代码:

#include<iostream>
using namespace std;
#define N 100000 + 5
int n; 
int a[N];
int b[N];
int ans = 0;
void merge(int L, int R) {
    int mid = (L + R) / 2;
    int l = L;
    int r = mid+1;
    int count = 0;
    while(l <= mid && r <= R) {
        if(a[l] < a[r]) {
            b[count++] = a[r++];    
        } else {
            b[count++] = a[l++];
            ans += R - r + 1;
        }
    }
    while(l <= mid) b[count++] = a[l++];
    while(r <= R) b[count++] = a[r++];
    for(int i = L; i <= R; i++) a[i] = b[i-L];
}
void MergeSortAndCount(int L, int R) {
    if(L < R) {
        int mid = (L + R) / 2;
        MergeSortAndCount(L, mid);
        MergeSortAndCount(mid + 1, R);
        merge(L, R);
    }
}
int main() {
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    MergeSortAndCount(0, n-1);
    printf("%d
", ans);
    return 0;
}
作者:kindleheart
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/kindleheart/p/9416210.html