有关归并排序和逆序对

●虽然是一个比较小的简单算法,但是我今天才学会2333可能真的是太垃圾了。。没关系 总结一下:
板子:

codevs 1688 求逆序对

题面就不写了,数据范围1e5(数字数量和数字大小) 所以要开long long

一开始没开long long然后炸了

这样子:
我最想说的,其实是在归并排序时,如何记录答案:
一开始我是这样想的:
对于要合并的有序序列A,B(A是原数列的左区间,B是右区间)如:
–>A:4 5 6
–>B:2 3 7 8
记两区间的开头两个数字分别为i,j
现在i和j分别是4,2,现在我要合并,j < i,这两个肯定是逆序对啦,那么我的答案应该怎么加呢?
我的错误的想法:
–>每次找到一组j < i时,那么j之前已经pop掉的那些数字一定 < i,所以我的答案应该是ans+=j-mid(j-(mid+1)+1),但我试了一下样例,发现输出了6,而答案应该是3,这样子肯定不对了。
为什么不对呢?
现在继续往下pop,我pop掉2,下一个数字是3,现在j=3,但是i=4没有变,我再次记录答案时,又把2加进去了 ,所以逆序对在这种情况下被重复记录了,显然是不对的。
那么我们应该怎样记录呢?
换一个思路–>
–>每次找到一组i,j,那么i以后的所有没有pop过的数字都和j组成逆序对,这时候,即使出现上述情况,答案也不会重复记录,也就是:ans+=mid-i+1;

这样子问题就解决啦~

代码很简单,递归实现merge_sort

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;

const int maxn=1e5+10;
ll n,ans=0;
ll a[maxn],t[maxn];

void done_sort(ll l,ll r,ll mid)
{
    ll i=l,j=mid+1;
    ll k=0;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j]) t[++k]=a[i++];
        else 
        {
            t[++k]=a[j++];
            ans+=mid-i+1;
        }
    }
    while(i<=mid) t[++k]=a[i++];
    while(j<=r) t[++k]=a[j++];
    for(ll num=1;num<=k;++num)
      a[num+l-1]=t[num];
}
void merge_sort(ll l,ll r)
{
    if(l<r)
    {
        ll mid=(r+l)>>1;
        merge_sort(l,mid);
        merge_sort(mid+1,r);
        done_sort(l,r,mid);
    }
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
    merge_sort(1,n);
    printf("%lld",ans);
    return 0;
}

记得一定要开long long!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值