逆序對
在數組A[x]中,若存在(i < j) && (A[i] > A[j]),則稱(A[i],A[j])為數組A[x]的一個逆序對
暴力O(N^2)求法
for(i = 1; i <= n; i ++)
for(j = i; j <= n; j ++)
if(A[i] > A[j])cnt ++;
歸並排序求法
由上面的表述可知,逆序對同樣可以表示為:在數組A[x]中,若存在(i > j) && (A[i] < A[j]),則稱(A[i],A[j])為數組A[x]的一個逆序對
那么,設cnt[i]為A[i]前比它大的數字個數,逆序對數則 = cnt[1] + cnt[2] + cnt[3] + ... + cnt[n];
一只總逆序對數為每個數字前比它大的數字個數之和,那么我們只需要在歸並排序中統計這個數字個數即可
#include<iostream>
...
void merge(int L, int R, int Mid){
int i = L;int j = Mid + 1;int k = L;
while(i <= Mid && j <= R){
if(s1[i] <= s1[j])s2[k ++] = s1[i ++];
else{
cnt += Mid - i + 1;
//對於i到Mid這Mid - i + 1個數字(假設它為j),每一個a[j]都與a[i]構成了一個逆序對
s2[k ++] = s1[j ++];
}
}
while(i <= Mid)s2[k ++] = s1[i ++];
//對於每個j都已討論完,每個j錢比他大的數都已計入cnt,所以此時不用更新cnt
while(j <= R)s2[k ++] = s1[j ++];
for(i = L; i <= R; i ++)s1[i] = s2[i];
}
void mergesort(int L, int R){
if(L < R){
int Mid = (L + R) / 2;
mergesort(L, Mid),mergesort(Mid + 1, R);
merge(L, R, Mid);
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i ++)scanf("%d", &s1[i]);
mergesort(1, n);
for(int i = 1; i <= n; i ++)printf("%d", s1[i]);
}
樹狀數組求逆序對
虛擬一個數組A[i],A[i] == 1表示出現了i,A[i] == 0表示沒有出現i
利用樹狀數組,getsum(i)表示i~i中出現的數字個數
邊輸入邊進行操作,對於當前的數字x,它所貢獻的逆序對書即為在它之前出現的比它大的數字的總數,即為當前x~n的sum值->getsum(n)-getsum(x);之后再將A[i] ++,更新C[i](modify)
#include<iostream>
#define lowbit(x) x & -x
...
void modify(int x, int d){
for(int i = x; i <= n; i += lowbit(i))c[i] += d;
}
int getsum(int x){
int sum = 0;
for(int i = x; i >= 1; i -= lowbit(i))sum += c[i];
return sum;
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i ++){
scanf("%d", &x);
ans += getsum(n) - getsum(x);
modify(x, 1)''
}
}