题目介绍:
给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。
输入格式:
第一行包含整数 n,表示数列的长度。
第二行包含 n个整数,表示整个数列。
数据范围:
n小于100000,所有的数据均小于int型最大值.
题目分析:
实际,我们要统计的是每个元素的后面比它小的元素的数量总和,记作全局变量count,
如果是直接暴力求解两层循环的话,会TLE。那么我们要如何做才能加快速度呢?
类同归并排序的思想,我们来看样例:
6
2 3 4 5 6 1
样例输出5; 仿照归并排序,把数组一分为二,“234” and “561”,看右边这一组
561->(“5”+“6”)+“1”,由于我们是要找序号靠前值更大的,每次就检查左子列是否有比右子列更大的元素。“5”(left)+“6”(right)->“56”,此时count=0,继续:“56”(left)+“1”(right),比较第一个值“5”和“1”的时候,5>1,由于左子列本身是升序,那么“5”之后的“6也同样会比“1”大,所以此时我们 count+=包括“5”在内的剩余的左子列元素个数,也就是2,所以此时count=2;最后的两个子列“234”(left)+“156”(right),“2”>“1”,所以count+=3,count=5,后面的就不再加了,最后得到count=5。但是这样算是不严谨的。
不妨假设:在计算“56”(left)+“1”(right)的时候,“1”之前还有k个元素,我们此时的“5”是大于“1”,由于右子列也是升序,那么“5”也同样大于包括“1”在内的右子列前面的元素个数k。
再有:如果出现右子列比较完了,而左子列还有剩余的情况,那么由于是递增数列,还应该加上除了左子列剩下的元素,需要注意这些元素都要乘右子列的个数
那么现在我们来总结一下,假设有两个子列:
left:
right:
现在我们确定了 那么 ;
如果此时 那么 ;
代码:
#include<stdio.h>
typedef long long LL; //此题最大值会爆int
#define N 100010
int n;
int q[N],tmp[N];
LL mergeSort(int l,int r)
{
if(l>=r) return 0;
int mid=l+r>>1;
LL res = mergeSort(l,mid)+mergeSort(mid+1,r); //分治的思想
int k=0,i=l,j=mid+1; //i扫左子列,j扫右子列
while(i<=mid&&j<=r)
if(q[i]<=q[j]) tmp[k++]=q[i++];
else
{
//res+=j-mid;
res+=mid-i+1;
tmp[k++]=q[j++];
}
while(i<=mid)
{
//res+=r-mid;
tmp[k++]=q[i++];
}
while(j<=r) tmp[k++]=q[j++];
for(int i=l,j=0;i<=r;i++,j++)q[i]=tmp[j];
return res;
}
int main()
{
int n,i;
scanf("%d",&n);
for(i=0;i<n;i++)scanf("%d",q+i);
printf("%lld",mergeSort(0,n-1));
return 0;
}
答案(y总)考虑的是对每一个q[j]来说前面有多少比它大的,我注释掉的部分是我自己想的,我初次做的时候我是想的考虑每一个q[i]后面有多少个比它小的,这样的话还需要考虑到当右子列停下来的时候,左子列还有剩余
我的想法很不幸的没有过,但是改成(y总)这样就过了.