逆序对的求法
对于一一个序列a,若i<j且a[i]>a[j],则称a[i]与a[j] 构成逆序对。
使用归并排序可以在\(O(nlogn)\)的时间里求出一一个长度为n的序列中逆序对的个数。归并排序每次把序列二分,递归对左右两半排序,然后合并两个有序序列。
递归对左右两半排序时,可以把左右两半各自内部的逆序对数作为子问题计算,因此我们只需要在合并时考虑“左边一半里一个较大的数”与“右边一半里一个较小的数”构成逆序对的情形,求出这种情形的个数。
合并两个有序序列a[l ~ mid] 与a[mid+1 ~ r]可以采用两个指针i与j分别对二者进行扫描的方式,不断比较两个指针所指向数值a[i] 和a[j]的大小,将小的那个加入到排序的结果数组中。若小的那个是a[j],则a[i ~ mid]都比a[j]要大,它们都会与a[j]构成逆序对,可以顺便统计到答案中。
const int N=1e5+10;
int a[N],temp[N];
int n;
LL res;
void merge(int l,int r)
{
int mid=l+r>>1;
int i=l,j=mid+1,k=0;
while(i<=mid && j<=r)
{
if(a[i] <= a[j]) temp[k++]=a[i++];
else
{
res+=mid-i+1;
temp[k++]=a[j++];
}
}
while(i<=mid) temp[k++]=a[i++];
while(j<=r) temp[k++]=a[j++];
for(int i=0;i<k;i++) a[l+i]=temp[i];
}
void mergeSort(int l,int r)
{
if(l>=r) return;
int mid=l+r>>1;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,r);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
mergeSort(0,n-1);
cout<<res<<endl;
//system("pause");
return 0;
}