一本通題解——1311:【例2.5】求逆序對

本題沒有什麼難度,一個模板題。之所以要寫這個題解,是因爲我自己第一次提交的時候,竟然沒有 AC。老子立馬懵逼了。後面耐心的分析了一下數據,才發現自己的錯誤在哪裏。透劇一下,掉到數據越界的深坑了。所以記錄一下。

題目

題目鏈接

一本通OJ:http://ybt.ssoier.cn:8088/problem_show.php?pid=1311

我的OJ:http://47.110.135.197/problem.php?id=4134

題目描述

給定一個序列 a1 , a2 , … , an,如果存在 i < j 並且 ai > aj,那麼我們稱之爲逆序對,求序列中逆序對的數目。

輸入

第一行爲 n,表示序列長度。
接下來一行中有 n 個元素,第 i 個數表示序列中的第 i 個數。

輸出

一行。所有逆序對總數。

樣例輸入

4
3 2 3 2

樣例輸出

3

數據範圍

1 ≤ N ≤ 10^5,
-10^5 ≤ ai ≤ 10^5。

分析

一個標準求逆序對模板題。

逆序對

設 A 爲一個有 n 個數字的有序集(n > 1),其中所有數字各不相同。如果存在正整數 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],則 <A[i], A[j]> 這個有序對稱爲 A 的一個逆序對,也稱作逆序數。

例如,數組(3,1,4,5,2)的逆序對有(3, 1),(3, 2),(4, 2),(5, 2),共 4 個逆序對。

算法思路

求數組的逆序對就是對數組進行排序,在排序過程中記錄逆序的數量即可。這樣我們需要使用穩定排序算法,比如冒泡排序、插入排序和歸併排序。

數據範圍分析

1、從題目中,我們可以知道 n 的範圍是 [1, 10^5],這樣我們就知道所有 O(n^2) 複雜度的排序算法肯定是 TLE,也就是說冒泡排序和插入排序不能使用,只有是要歸併排序。

2、假設最壞的情況,也就是這 10^5 個數據全部是倒序,那麼逆序對應該是 10^{5}+(10^{5}-1)+(10^{5}-2)+\cdots +2+1=\frac{10^{5}*(10^{5}+1)}{2}=5*10^{9}+5*10^{4}。這個數據什麼意思?我們來回憶一下 C++ 數據類型 unsigned int 的最大範圍是 4,294,967,295,也就是 4.2*10^{9},說明 unsigned int 已經容納不下這個數據。太太太坑爹了。

歸併排序求逆序對

這個部分是分析的核心。首先我們用一個樣例數據來說明,使用歸併排序。假設我們有一個數列 [5 4 2 6 3 1],使用歸併排序來看一下有幾個逆序對。

第一步:二分,如下圖所示。逆序對數量 = 0。

第二步:第四層 5 和 4 合併:i=l=1; r=2; mid=(l+r)/2=1; j=mid+1=2; 因爲 5>4,逆序對數量+mid-i+1=1。b數組:[4 5 0 0 0 0],j+1>r;退出;a 數組 [4 5 2 6 3 1]。如下圖所示。

第三步:第四層 6 和 3 合併:i=l=4; r=5; mid=(l+r)/2=4; j=mid+1=5; 因爲 6>3,逆序對數量+mid-i+1=2。b數組:[4 5 0 3 6 0],j+1>r;退出;a 數組 [4 5 2 6 3 1]。如下圖所示。

第四步:第三層 4,5 和 2 合併:i=l=1; r=3; mid=(l+r)/2=2; j=mid+1=3; 因爲 4>2,逆序對數量+mid-i+1=4。b數組:[2 4 5 3 6 0],j+1>r;退出;a 數組 [2 4 5 3 6 1]。如下圖所示。

第五步:第三層 3,6 和 1 合併:i=l=4; r=6; mid=(l+r)/2=5; j=mid+1=6; 因爲 3>1,逆序對數量+mid-i+1=6。b數組:[2 4 5 1 3 6],j+1>r;退出;a 數組 [2 4 5 1 3 6]。如下圖所示。

第六步:第二層 2,4,5 和 1,3,6 合併:i=l=1; r=6; mid=(l+r)/2=3; j=mid+1=4; 因爲 2>1,逆序對數量+mid-i+1=9。b數組:[1 2 4 5 3 6],j+1;對應 2<3,b數組:[1 2 4 5 3 6],i+1;對應 4>3,逆序對數量+mid-i+1=11,b數組:[1 2 3 4 5 6]。j+1;對應 4<6,b數組:[1 2 3 4 5 6];i+1,5<6,b數組:[1 2 3 4 5 6];i+1>mid,退出,a 數組 [1 2 3 4 5 6],有序。如下圖所示。

從上面的分析可以看出,使用歸併排序求逆序對,最核心的是下面這句話

ans+=mid-i+1;

AC 參考代碼

//http://ybt.ssoier.cn:8088/problem_show.php?pid=1311
//求逆序對
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e6+4;
int nums[MAXN] = {};
int tmps[MAXN];
long long ans = 0;

//使用歸併排序
void merge(int l, int mid, int r) {
    int i=l;
    int j=mid+1;
    int k=l;
    while (i<=mid && j<=r) {
        if (nums[i]>nums[j]) {
            tmps[k++]=nums[j++];
            ans+=mid-i+1;
        } else {
            tmps[k++]=nums[i++];
        }
    }

    while (i<=mid) {
        tmps[k++]=nums[i++];
    }
    while (j<=r) {
        tmps[k++]=nums[j++];
    }
    for (i=l; i<=r; i++) {
        nums[i]=tmps[i];
    }
}

void mergeSort(int l, int r) {
    int mid;
    if (l < r) {
        mid = l+((r-l)>>1);
        mergeSort(l, mid);
        mergeSort(mid+1, r);
        merge(l, mid, r);
    }
}

int main() {
    int n;
    cin >> n;
    int i;
    for (i=0; i<n; i++) {
        cin >> nums[i];
    }

    mergeSort(0, n-1);

    cout << ans << endl;

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章