洛谷P1908 逆序对 (归并排序串谈)

链接:P1908

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

输入格式
第一行,一个数n,表示序列中有n个数。

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

输出格式
给定序列中逆序对的数目。

输入输出样例
输入 #1
6
5 4 2 6 3 1
输出 #1
11
说明/提示
对于25%的数据,n \leq 2500n≤2500

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

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

请使用较快的输入输出

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


这道题不二分过不去数据范围太大了T_T
二分的思想就是分而治之
而我们可以明显的看出来,如果我们要用二分来查找一下逆序对
这个过程其实就是归并排序的过程
那么什么是归并排序
本图转自https://www.cnblogs.com/chengxiao/p/6194356.html
我们可以举几个数字来列举一下
比如有一个数列:

5 4 7 6 3 9

那么如果我们已经进入了归并排序的最后一步,此时已经分为了两个有序序列

4 5 7
3 6 9

那么我们便用4与第二个数组里面的内容全部比较一下,因为我们已
我们发现了4要比第二个数组的第一个数要大,那么肯定 3 一定会比第一个数组的其他数都要小(根据有序性)同时当前数组也是有序的,因此一定小于同数组的后面的数
那么我们就把3放入最终排序结果ans的数组的第一位上

ans[1]=3
此时
4 5 7
6 9

  1. 那么这个时候我们再用4与6比较,发现4 小了,根据有序性我们需要把4放入排序结果的数组中
  2. 因为4不见了,那么我们便用第一个数组中的第二个数5比较,我们发现了5也是小于6的根据有序性,则把5也放入排序结果的数组中
  3. 接下来我们再用第一个数组中的7比较,此时7大于6,那么根据有序性,我们就把6放入排序结果的数组中
  4. 接下来7再与9比较,此时9>7则把7放入存储结果的数组中 因为第一个数组没有数了
  5. 那么我们就跳出这个比较过程,再把第二个数组的所有数自动放在存储结果数组的队尾即可

若用归并排序的思想解决此题,我们同样也是如此
我们做一个从小到大归并排序,然后再刚才一个一个数字比较的过程中,如果第一个数组的数大于了第二个数组的某一个数,那么则说明出现了逆序对的现象同时由于第一个数组本身就有序性,因为这个数已经大于了第二个数组的中的数a,那么在这个数后面的数也一定大于了第二个数组中的数a
那么如果我们发现了第一个数组的数大于了第二个数组的某一个数的情况
我们在更新存储结果的数组的同时,同时把记录存在逆序对的个数ans也更新一下
因为两个数组的分别上下界为:
第一个:L~ mid
第二个:mid+1~R
所以我们可以推出记录逆序对的个数
每次ans+=mid-i+1 (i为发现第一个数组的第i个数大于了第二个数组的某一个数)
最后输出这个ans即可

代码:

#include<cstdio>
#include<iostream>
using namespace std;
int n,a[1000001],c[1000001];
long long ans;
void CZP(int L,int R)  //归并排序的过程
{
    if(L>=R)  
        return;
    int mid=(L+R)/2,i=L,j=mid+1,k=L;
    CZP(L,mid),CZP(mid+1,R);
    while(i<=mid&&j<=R)
    {
        if(a[i]<=a[j])
        {
            c[k]=a[i];
            k++;i++;
        }
        else   //出现了逆序对
        {
            c[k]=a[j];
            k++;
            j++;
            ans+=mid-i+1;  //累加逆序对个数
        }
    }
    while(i<=mid){
         c[k]=a[i];
         k++;
         i++;
    }
    while(j<=R){
        c[k]=a[j];
        k++;
        j++;
    }
    for(int l=L;l<=R;l++)  //更新原来数列,使得有序化
        a[l]=c[l];
} 
int main()
{
    scanf("%d",&n); 
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    CZP(1,n);
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值