考慮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\n", ans); return 0; }