求解逆序对一般有三种解法:暴力,归并排序,树状数组
1、暴的复杂度O(n^2),一般题目基本不会就让你这么水过的。所以下面两种方法才是正解。
2、归并排序:一般教科书归并排序都是最基本的,mergeSort(), merge(), copy()都不难理解,只不过对于逆序对问题,我们应该到,一段序列[left, right]中的逆序对个数应该等于[left, mid]中的逆序对数和[mid+1, right]中的逆序对数以及存在于两者之间的逆序对数;
merge()函数是将[left, mid]和[mid+1, right]进行合并排序,需要意识到,这是两子段都是已经排好序的了,所以一旦a[i]>a[j],left<=i<=mid,mid+1<=i<=right,则a[i]~a[mid]都大于a[j],即存在mid-i+1个逆序对。
代码如下:
/*逆序对:归并排序*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 500010;
int arr[maxn], temp[maxn];
int sum;
void _merge(int Left, int Mid, int Right)
{
int i = Left, j = Mid + 1, flag = Left;
while(i <= Mid && j <= Right){
if(arr[i] > arr[j])
{
temp[flag++] = arr[j++];
sum += Mid-i+1;
}
else
{
temp[flag++] = arr[i++];
}
}
while(i <= Mid)
{
temp[flag++] = arr[i++];
}
while(j <= Right)
{
temp[flag++] = arr[j++];
}
}
void _copy(int Left, int Right)
{
for(int i = Left; i <= Right; i++)
{
arr[i] = temp[i];
}
}
void mergeSort(int Left, int Right){
if(Left < Right)
{
int Mid = (Left + Right) >> 1;
mergeSort(Left, Mid);
mergeSort(Mid+1, Right);
_merge(Left, Mid, Right);
_copy(Left, Right);
}
}
int main()
{
int n;
while(~scanf("%d", &n) && n)
{
sum = 0;
for(int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
mergeSort(0, n-1);
printf("%d\n", sum);
}
return 0;
}