逆序对的求法

对于一一个序列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;
}
posted @ 2021-03-27 21:32  Dazzling!  阅读(217)  评论(0编辑  收藏  举报