逆序对数量

题意


给定一个长度为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;
}
posted @ 2021-03-11 15:53  冰糖ryj  阅读(129)  评论(0编辑  收藏  举报