逆序对数量
题意
给定一个长度为n的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i < j 且 a[i] > a[j],则其为一个逆序对;否则不是。
输入格式
第一行包含整数n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1≤n≤100000
输入样例:
6
2 3 4 5 6 1
输出样例:
5
分析
逆序对是说排在前面的数字比后面大,关键是不重不漏的统计数组中所有逆序对。
归并排序每次操作对数组一分为二,刚开始先一直递归到最底层,然后再一层层向上合并,我们可以在合并的过程时判断两部分之间存在逆序对。其中,每一部分的逆序对数量已经在之前的递归操作中统计出来了,即 \([l,mid]\) 和 \([mid+1,r]\) 这两部分的逆序对已经被统计,在本次只需要统计这两部分之间存在逆序对,就可以完全统计出 \([l, r]\) 之间所有的逆序对。
具体来看,如果 \([l, mid]\) 中的一个某个数 \(a[i]\) 大于 \([mid+1, r]\) 中的某个数 \(a[j]\),那么 \([i, mid]\) 这个区间内 \(mid-i+1\) 个数,都可以和 后半区间的数字 \(a[j]\) 形成逆序对。
代码
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int a[N],w[N];
ll merge(int l,int r)
{
if(l>=r)return 0;
int mid=l+r>>1;
ll ans=merge(l,mid)+merge(mid+1,r);
int i=l,j=mid+1,cnt=0;
while(i<=mid&&j<=r)
{
if(a[i]<=a[j])w[cnt++]=a[i++];
else w[cnt++]=a[j++],ans+=mid-i+1;
}
while(i<=mid)w[cnt++]=a[i++];
while(j<=r)w[cnt++]=a[j++];
for(int i=l,j=0;i<=r;i++,j++)a[i]=w[j];
return ans;
}
int main()
{
int n;scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
ll ans=merge(0,n-1);
printf("%lld\n",ans);
return 0;
}