题目大意:
给你一个n,和n个数a1....an,请你求出这个序列的逆序对个数,n<=65537,ai<=1e9
解题思路:
首先,O(n^2)的做法我就不多说了,不会写的跪键盘吧。。。。。。
但是对与n的数据大小,显然过不了(其实是我没去想,如果认为过的了得请自己试,但是我们是有追求的人),所以我们需要用到O(nlogn)的做法。
相信大家都有一定了解,就是用归并排序来完成对逆序对的查找。
那么具体如何实现呢?其实很简单:
首先归并排序大家总会吧(不会的我也没办法,因为我也不会。。。。)
我们都知道归并排序中有一个对两个有序的序列合并的操作,我们就是要利用这个操作来求逆序对。
首先我们假设两个有序的序列为b1,b2,假设序列b1在序列b2前面,那么我们就只需要求出b1与b2的逆序对的个数就行了。
这其实是用到了分治的思想,显然数列内部的不需要考虑了,因为我们求出这两个数列的时候已经求出了序列内部的逆序对个数了。
我们发现b1,b2已经是有序的了,所以对于b2[ j ]当我们找到一个最小的b1[ i ]使得b2[ j ]<b1[ i ],那么从i到b1的最后一个元素都可以与b2[ j ]组成逆序对,所以我们只需要在合并时,若b2[ j ]加入合并的数组,这是b1下标为i,那么逆序对个数就应该加入从i到b1数组的最后一个元素的个数,这样我们就可以再归并排序的时候就可以求出逆序对了。
附上代码:
#include <stdio.h>
//我是淳朴的C党
int n,a[66000]={0};
long long sum=0;
int b[66000]={0};
void done(int l,int r)
{
int i,j,p;
int mid=(l+r)/2;
if(l>=r)
return;
done(l,mid);
done(mid+1,r);
for(i=l,j=mid+1,p=l;i<=mid && j<=r;)
{
if(a[i]<=a[j])
b[p++]=a[i++];
else
{
b[p++]=a[j++];
sum+=mid-i+1;
}
}
for(;i<=mid;)
b[p++]=a[i++];
for(;j<=r;)
b[p++]=a[j++];
for(i=l;i<=r;i++)
a[i]=b[i];
return;
}
int main()
{
int i;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
done(1,n);
printf("%I64d\n",sum);
return 0;
}