洛谷 P1908 逆序对 归并排序

20 篇文章 0 订阅
11 篇文章 0 订阅

https://www.luogu.org/problemnew/show/P1908

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
Update:数据已加强。

输入输出格式

输入格式:

 

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。序列中每个数字不超过10910^9109

 

输出格式:

 

给定序列中逆序对的数目。

 

输入输出样例

输入样例#1: 复制

6
5 4 2 6 3 1

输出样例#1: 复制

11

说明

对于25%的数据,n≤2500

对于50%的数据,n≤4×10^4

对于所有数据,n≤5×10^5

请使用较快的输入输出

应该不会n方过50万吧 by chen_zhe

思路:逆序对数与冒泡排序的交换次数是相等的,但是很明显O(n^2)会炸,聪明的人类后来想到了分治法,只需稍微修改归并排序,就可以得到答案。归并排序将数组分成左右两部分,再将两部分合并起来。(当然 这个操作是递归的)那么我们只要知道了左半部分的逆序对数和右半部分的逆序对数,再加上合并过程中计算出的逆序对数不就得到答案了吗?关键就在于如何在合并过程中计算出逆序对数,假设当前合并过程中,左半部分的指针是i,右半部分的指针是j(下标),且左半部分一共有n1个元素,那么在L[i]>R[j]的时候,根据逆序数的定义,我们可以知道这时候贡献了n1-i个逆序数。(注意左半部分实际上是[0,n1)即左闭右开)那么问题就简单了。

#include<iostream>
#include<cstdio>
using namespace std;

const int MAX=500005;
int a[MAX];
int L[MAX/2];
int R[MAX/2];

long long Merge(int l,int mid,int r);
long long MergeSort(int l,int r);

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    cout<<MergeSort(0,n)<<endl;
    return 0;
}

long long Merge(int l,int mid,int r)
{
    long long num=0;
    int temp1=mid-l;
    int temp2=r-mid;
    for(int i=0;i<temp1;i++)
        L[i]=a[l+i];
    for(int i=0;i<temp2;i++)
        R[i]=a[mid+i];
    int i=0,j=0,k=l;
    while(i<temp1&&j<temp2)
    {
        if(L[i]<=R[j])
            a[k++]=L[i++];
        else
        {
            num+=temp1-i;
            a[k++]=R[j++];
        }
    }
    while(i!=temp1)
        a[k++]=L[i++];
    while(j!=temp2)
        a[k++]=R[j++];
    return num;
}

long long MergeSort(int l,int r)
{
    if(l+1<r)//最少两个元素
    {
        int mid=(l+r)/2;
        long long n1,n2,n3;
        n1=MergeSort(l,mid);
        n2=MergeSort(mid,r);
        n3=Merge(l,mid,r);
        return n1+n2+n3;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值