逆序对的数量

八.逆序对的数量

当前一个数比后一个数严格大(即不会相同)的时候,就称两个数为逆序对

也用到了分治的思想。首先看归并的基本思想:

1.将整个区间一分为二。[L,R]=>[L,mid],[mid+1,R]

2.递归处理两个子区间

3.归并两个序列

然后将所有可能存在的逆序对视为三大类。1.两个数同时出现在左区间2.同时出现在右半边3.一个左一个右。

接下来分情况讨论(假设归并的同时能统计三种情况,则逆序对数量为三种情况的数量之和:

1.第一种情况:左半边内部逆序对数量,其个数应为merge_sort(L,mid)

2.第二种情况:右半边……merge_sort(mid+1,R)

3.第三种情况:左右两个区间,首先得到右半边区间第一个数,接下来统计左边有多少个数大于它(即构成逆序对),记为S1。然后是右边第二个数……记为S2。直到第m个数记为Sm。

快速算出S的思路:用两个指针指向左右两个区间的数,左区间的往后挪,当某个数开始左区间的数比右区间指向的数大时,其之后的数必然比右区间目前所指向的数大(因为左右区间都已经排序好了)。假设此时为第j个数,则Sj为mid-i+1。

因此,当我们进行归并排序时,每次需要输出q[j]的时候,只需要在最终答案中加上一个mid-i+1即可,这就是第三种情况的数量统计。

//注意数据范围,当1<=n<=100000时逆序对数量可能会爆int,所以用long int存
#include<iostream>
 
using namespace std;
 
typedef long long LL;//偷懒而已
 
const int N=100010;
 
int n,q[N];
 
LL merge_sort(int l,int r){
    if(l>=r) return 0;
    
    int mid=l+r>>1;//  >>符号优先级小于+所以不用加括号
    LL res=merge_sort(l,mid)+merge_sort(mid+1,r);
    
    //归并的过程
    int k=0,i=l,j=mid+1;
    int tmp[100010];
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) tmp[k++]=q[i++];
        else {tmp[k++]=q[j++];res+=mid-i+1;}
    }
    //扫尾,左右一起扫尾
    while(i<=mid) tmp[k++]=q[i++];
    while(j<=r) tmp[k++]=q[j++];
    //物归原主
    for(int i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
    
    return res;
}
 
int main(){
    cin>>n;
    for(int i=0;i<n;i++) cin>>q[i];
    
    cout<<merge_sort(0,n-1)<<endl;
    
    return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值