AcWing 超快速排序

 

每发现相邻数字顺序颠倒即将其交换,此时序列的逆序数减一.所以求出给定序列逆序和即可.

求逆序和最简单的方法是冒泡排序,但是O(n2)是很不好的算法.

常用的求逆序和方法有归并排序和树状数组,这里使用归并排序方法.实现见此模板.(假设数组下标从1开始)

void merge(int l, int r){
    if(l == r) return;
    int mid = l + r >> 1;
    merge(l, mid);
    merge(mid + 1, r);
    int i = l, j = mid + 1;
    for(int k = l; k <= r; k++)
        if(j > r || i <= mid && s[i] < s[j]) tmp[k] = s[i++];    // 注意如果序列中有重复数字需要改为s[i] <= s[j]
        else tmp[k] = s[j++], ct += mid - i + 1;
    for(int k = l; k <= r; k++) s[k] = tmp[k];
}

执行该函数后,数组s将被按升序排列,且ct(假设初始化为0)的值即为原序列的逆序数.

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

long long s[500010], b[500010], n, ct;

void merge(int l, int r){
    if(r <= l) return;
    int mid = l + r >> 1;
    merge(l, mid);
    merge(mid + 1, r);
    int i = l, j = mid + 1;
    for(int k = l; k <= r; k++)
        if(j > r || i <= mid && s[i] < s[j]) b[k] = s[i++];
        else b[k] = s[j++], ct += mid - i + 1;
    for(int i = l; i <= r; i++) s[i] = b[i];
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    while(cin >> n){
        if(!n) break;
        ct = 0;
        for(int i = 1; i <= n; i++) cin >> s[i];
        merge(1, n);
        cout << ct << endl;
    }

    return 0;
}
AC Code
posted @ 2020-12-21 14:10  goverclock  阅读(138)  评论(0编辑  收藏  举报