逆序对
在数组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)''
}
}