解题思路
(1) 归并排序:
它的基本思想是:将待排序的数列分成两个小的数列,先对两个子集进行排序,然后进行两个有序子集的合并,形成排序后的数一列,然后对子的处理方法与刚才的处理方法是一致的,直到子集中只存在一个整数为止。
(2)计算逆序对
归并排序即是将数列分为长度相等或相差1的两段,分别递归进行归并排序,然后两段成为两个有序队列,每次在两个队列中选取队首元素的较小值,排成一列后就得到有序的数列.
如果逆序对的两个元素在一次拆分中被分到了同一边,我们暂时不必理会,因为随着递归的进行,总有一天他们会被拆开,这时只用统计出两个元素分开在两段时出现的逆序对.具体的做法是,如果后一段的队首元素小于前一段的队首元素,则它于前一段所有剩下的元素构成逆序对,因此累加前一段剩下的元素的个数.因为我们只是在归并排序的过程中顺便统计了逆序对,所以时间复杂度为归并排序的复杂度即O(nlgn)
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int n,a[500010];
long long ans;
void work(int l,int mid,int r)//归并排序
{
int i=l,j=mid+1,tot=l,b[500010];
while(i<=mid&&j<=r)
{
if(a[i]<=a[j])
b[tot++]=a[i++];
if(a[i]>a[j])
b[tot++]=a[j++],ans+=mid-i+1;//统计答案
}
while(i<=mid)b[tot++]=a[i++];
while(j<=r)b[tot++]=a[j++];
for(int i=l;i<=r;i++)
a[i]=b[i];
}
void gbsort(int l,int r){
if(l>=r)return;
int mid=(l+r)/2;
gbsort(l,mid);
gbsort(mid+1,r);
work(l,mid,r);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
gbsort(1,n);
printf("%lld",ans);
}