做这道题本来是想练习线段树的..但发现在用归并排序求出原始数列的逆序对后...其他数列的逆序对可以通过二分查找的方式得出.
比如原数列: 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; }