【分治】——求逆序对的个数

Description

给定一个长度为N的int型数组a[0,1,2,...N-1], 请计算逆序对个数.当i<j且a[i]>a[j], 则称a[i]与a[j]是一对逆序对.

Input

第一行输入M表示包含M组测试数据,每组先输入N (N<=50000), 接着输入N个int型整数.

Output

输出逆序对个数.

Sample Input

2
5 1 5 2 1 3
6 85 16 44 99 66 1

Sample Output

4
9

方法一:暴力模拟

直接按题意来进行模拟,两层循环,遇见A[i]>A[j]将计数器+1,最后输出结果,时间复杂度为O(n2)。必然超时。

代码略。

方法二:分治+归并排序

将逆序对的定义整理一下:若存在(i<j)&&(A[i]>A[j]),则称(A[i],A[j])为数组A的一个逆序对。

因此,我们可以利用排序算法,在排序的过程中统计逆序对的个数。这里我们采用归并排序。理由是归并排序在归并的过程中本质是将后面的较小数与前面的较大数位置互换,而我们正是要求i<j时A[i]>A[j]的组合个数,可以利用这一点将时间复杂度降为O(n logn)。

#include<bits/stdc++.h>
typedef long long ll;
ll Num=0;

void merge(int l,int r,int m,int* f,int* mf){
    int i=l,j=m+1,k=l;
    while(i<=m&&j<=r){
        if(f[i]<=f[j]){
            mf[k++]=f[i++];
        }
        else{
            mf[k++]=f[j++];
            Num+=m-i+1;
        }
    }
    while(i<=m) mf[k++]=f[i++];
    while(j<=r) mf[k++]=f[j++];
    for(int i=l;i<=r;++i) f[i]=mf[i];
}

void mergeCount(int l,int r,int* a,int *A){
    if(l<r){
        int mid=(l+r)/2;
        mergeCount(l,mid,a,A);
        mergeCount(mid+1,r,a,A);
        merge(l,r,mid,a,A);
    }
}

void solve(){
    int N;
    std::cin>>N;
    int f[N];
    for(int i=0;i<N;++i){
        std::cin>>f[i];
    }
    int mf[N]={0};
    mergeCount(0,N-1,f,mf);
    std::cout<<Num<<'\n';
}

int main(){
    int M;
    std::cin>>M;
    while(M--){
        solve();
        Num=0;
    }
    return 0;
}

 

posted @ 2022-05-02 12:06  天涯海角寻天涯  阅读(235)  评论(0)    收藏  举报