做这道题本来是想练习线段树的..但发现在用归并排序求出原始数列的逆序对后...其他数列的逆序对可以通过二分查找的方式得出.
比如原数列: 1 3 6 9 0 8 5 7 4 2 计算出其逆序对的个数为22
左移: 3 6 9 0 8 5 7 4 2 1
实际上是将1挪到了数列的最右..二分查找有多少个数大于1..二分查找有多少个数小于1...那么直接退出其逆序数为 22 - 1 + 8 = 29
Program:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<set>
#include<ctime>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
#define oo 100000007
#define ll long long
#define pi acos(-1.0)
#define MAXN 5005
using namespace std;
int n,a[MAXN],b[MAXN],temp[MAXN];
ll data;
void merge(int s1,int e1,int s2,int e2)
{
int x,i,j,num=0;
i=s1,j=s2;
for (x=s1;x<=e2;x++)
{
if (i>e1) temp[x]=a[j++];
else
if (j>e2) temp[x]=a[i++];
else
if (a[i]>a[j])
{
temp[x]=a[j++];
data+=e1-s1+1-num;
}else temp[x]=a[i++],num++;
}
for (i=s1;i<=e2;i++) a[i]=temp[i];
return;
}
void merge_sort(int l,int r)
{
if (l==r) return;
int mid=(l+r)/2;
merge_sort(l,mid);
merge_sort(mid+1,r);
merge(l,mid,mid+1,r);
return;
}
int main()
{
int i;
ll ans;
while (~scanf("%d",&n))
{
for (i=1;i<=n;i++) scanf("%d",&a[i]);
memcpy(b,a,sizeof(a));
data=0;
merge_sort(1,n);
ans=data;
for (i=1;i<n;i++)
{
int l,r,mid;
l=0,r=n+1;
while (r-l>1)
{
mid=(l+r)/2;
if (a[mid]<=b[i]) l=mid;
else r=mid;
}
data-=l-1;
l=0; r=n+1;
while (r-l>1)
{
mid=(l+r)/2;
if (a[mid]<b[i]) l=mid;
else r=mid;
}
data+=n-r;
ans=min(ans,data);
}
printf("%I64d\n",ans);
}
return 0;
}