网上看了一些归并排求逆序对的文章,又看了一些树状数组的,觉得自己也写一篇试试看吧,然后本文大体也就讲个思路(没有例题),但是还是会有个程序框架的
好了下面是正文


  1. 归并排求逆序对
  2. 树状数组求逆序对

一、归并排求逆序对

温馨提示:阅读这段内容需要的知识点:归并排序

— 首先的话,归并排序大家应该都知道的吧?归并排是利用分治的思想,先分后和,分到左右区间相等或相交时在返回上一层进行两个有序小数组交错插入排序,形成一个有序数组,然后层层返回排好序的数组,作为新的小数组插入大数组排序,这就是一个n log n的排序算法(带 log 的算法一般都算是比较快的,只要常数不过大)。然后还是不懂的同学可以百度,这里不细讲了。另外提一提,实在是不会用归并排的话冒泡也是一样可以求逆序对的,累加的话就变成了判断到需要交换时进行,但冒泡的复杂度高了点,是 n^2 了,提交 后会爆几个点就不知道了,得看具体题目和数据。(反正数据一般不会水到让你满分【斜眼笑ing】)

— 其次的话,用归并排求逆序对无非也就是在插入的过程中将 逆序数 ans 累加,然后也没什么不同的了,只要记得归并排模板的话基本也是码的出来的。(个人感觉归并排求逆序队还是挺清晰的,因为这样基本就是套套模板不用想太多)

模板如下,但请别直接复制粘贴,好歹自己打一遍

int n,ans;
const int mod=;
int f[],g[]; void merge_sort(int l,int r)
{
if(l>=r) //如果说l、r交错的话直接return不管
return ; int mid=(l+r)>>; //以l、r的中点为界向下分支排序
merge_sort(l,mid);
merge_sort(mid+,r); int i=l,j=mid+,k=l;
while(i<=mid && j<=r) //保证两个小的数组不超边界
{
if(f[i]<f[j])
g[k++]=f[i++];
else
{ //大概要在模板上做修改的就是这块了,用ans把逆序对累加
ans=(ans+mid-i+)%mod; //如果题目中有取余就%mod
g[k++]=f[j++];
}
}
// 然后把剩下的数直接插入到大的数组末尾(但不会对ans进行累加操作)
while(i<=mid)
g[k++]=f[i++];
while(j<=r)
g[k++]=f[j++];
for(int i=l;i<=r;++i) //g数组只是一个中间量,用完就丢了,f才是要排序的数组
f[i]=g[i]; }
 

二次分析

–然后我觉得还得解释一下为什么ans在j数组(即第二个小数组)中的值插入到达数组的时候才累加。试想,逆序对就是大的数字在前面,小的数字在后面,每次发现一组这样的数字对那么整个数组中的逆序对数量就可以+1了。
如:1 2 6 8 和 3 5 7 9 ,初始i指向1,j指向3,k指向8,l指向9,ans=0
在第一次比较时,1<3,则1插入进大数组,i++,ans不变
第二次比较式,i指向了2, 2<3,则2插入进大数组,还是i++,ans不变
第三次,i指向6,6>3,3入大数组,j++,ans+=2 。
这里就是重点了,3小于6,则3也一定小于6后面的数,并且可以和这些数(共两个)分别对应形成n个逆序对(n为k-i+1,即6和6的后面总共还剩多少个数)
第四次也一样,是j++,ans+=2,此时ans为4,原理同上,不再解释
然后就是继续向大数组队尾插入数了,我们发现直到 i 数组为空时(全被插入完毕了),j 数组仍有剩余,那么就将 j 数组直接插入进大数组,但ans不进行累加(因为此时 i 数组空了,无法与 j 数组中剩下的数形成逆序对)

呼~这样总该解释的差不多了,同志们自个儿好好消化消化吧。

二、树状数组求逆序对

温馨提示:阅读本段需要具备的知识点:树状数组的基本操作(update、getsum、lowbit之类的)

–首先的话,树状数组我也不来说这么详细了,许多细节方面(如 getsum 时x为什么要减去一个lowbit(x)了之类的)的理解就麻烦请自己思考得出或是去问百度了。

–其次的话,树状数组其实就是代码短一点(短一点就好码一点,好码一点就好调试一点,好调试一点就不容易出错一点),看着舒服吧。
然后我就不啰嗦了,直接上代码吧。

int lowbit(int x)       //lowbit求最末尾的1所在的位置
{
return x&(-x);
} void update(int x,int k) //update等会儿讲
{
for(;x<=n;x+=lowbit(x))
g[x]+=k;
} long long getsum(int x) //getsum的话。。。也等会儿讲
{
long long res=;
for(;x;x-=lowbit(x)) //一直跳向比x小的数,如7->6->4->0(结束)
//或是6->4->0(结束)
res+=g[x];
return res;
} void BIT() //这个BIT啊,我看书的时候也不知道是什么鬼,
//然后才发现原来是树状数组英文名(Binary Indexed Trees)的缩写
{
for(int i=;i<=n;++i)
{
update(f[i],);
ans=(ans+f[i]-getsum(f[i]))%mod;
}
}

另外提一点,不要看着这个代码好像行数很多,码一遍之后会发现真的很短

然后讲讲update和getsum吧(主要是给学过的人讲,谈谈我的理解)

上图!


在这里的话,你可以认为每个三角形的顶端都是一个BOSS,一旦他们的下属出现了之后,下属会先+1,再逐级向上汇报(也就是说有小三角形的话就先向小三角形上的BOSS先汇报,然后再由这个小的BOSS向更大的BOSS汇报,直到顶层), 这样的话我们最后就可以清晰地得到一个实时更新的树状数组,每个g中所存的就是它以及它的下属目前已出现的个数

这里的话 6 在getsum的时候路径为6->4->0(结束),得到的 res 为 1,即已出现的数字中,小于等于6的数字只有一个

那么上面演示的是有逆序对的情况,同样的,你也可以自行演示一下 先 2 后 6的情况,这时候你会发现 ans 并没有累加,即没有逆序对的情况……
总之,还是要熟知树状数组操作里的含义吧。

好了,心血来潮写的一篇博客终于搞定了。(大概花了两个多小时的样子,是不是蒟蒻?)
然后如果说有哪里我讲的不对的话,欢迎各位 神(da)犇(lao)在评论区里喷我。_ (:зゝ∠) _
bye bye(下次见)!
1000010 1011001 1000101————!(一串ASCII码)

求逆序对 ----归并排 & 树状数组的更多相关文章

  1. codevs 4163 求逆序对的数目 -树状数组法

    4163 hzwer与逆序对  时间限制: 10 s  空间限制: 256000 KB  题目等级 : 黄金 Gold 题目描述 Description hzwer在研究逆序对. 对于数列{a},如果 ...

  2. BZOJ_3295_[Cqoi2011]动态逆序对_CDQ分治+树状数组

    BZOJ_3295_[Cqoi2011]动态逆序对_CDQ分治+树状数组 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一 ...

  3. 51 Nod 1107 斜率小于0的连线数量 (转换为归并求逆序数或者直接树状数组,超级详细题解!!!)

    1107 斜率小于0的连线数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题   二维平面上N个点之间共有C(n,2)条连线.求这C(n,2)条线中斜率小于0的线 ...

  4. bzoj3295: [Cqoi2011]动态逆序对(cdq分治+树状数组)

    3295: [Cqoi2011]动态逆序对 题目:传送门 题解: 刚学完cdq分治,想起来之前有一道是树套树的题目可以用cdq分治来做...尝试一波 还是太弱了...想到了要做两次cdq...然后伏地 ...

  5. 2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对)

    2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对) https://www.luogu.com.cn/problem/P5041 题意: 给一个字符串 \(S\) ,每 ...

  6. 求逆序对常用的两种算法 ----归并排 & 树状数组

    网上看了一些归并排求逆序对的文章,又看了一些树状数组的,觉得自己也写一篇试试看吧,然后本文大体也就讲个思路(没有例题),但是还是会有个程序框架的 好了下面是正文 归并排求逆序对 树状数组求逆序对 一. ...

  7. hdu 1394 Minimum Inversion Number(逆序数对) : 树状数组 O(nlogn)

    http://acm.hdu.edu.cn/showproblem.php?pid=1394  //hdu 题目   Problem Description The inversion number ...

  8. Dynamic Inversions II 逆序数的性质 树状数组求逆序数

    Dynamic Inversions II Time Limit: 6000/3000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Other ...

  9. 用归并排序或树状数组求逆序对数量 poj2299

    题目链接:https://vjudge.net/problem/POJ-2299 推荐讲解树状数组的博客:https://blog.csdn.net/int64ago/article/details/ ...

随机推荐

  1. layui(七)——rate组件常见用法总结

    layui中提供了rate组件,用法很简单,直接上代码. <div id="test1"></div> <script> layui.use(' ...

  2. vs code配置git

    在项目目录执行 git init 修改.git文件夹下的config文件 [core] repositoryformatversion = 0 filemode = false bare = fals ...

  3. Sqlcmd使用详解

    Sqlcmd实用工具,可以输入 TRANSACT-SQL 语句. 系统过程和脚本文件,通过各种可用模式: 通过命令提示符. 在中查询编辑器在 SQLCMD 模式下. 在 Windows 脚本文件. 在 ...

  4. 简单备份mysql数据库

    对于数据量不大的业务场景,可以每天做全量备份. 实现方式:编写备份数据库脚本,然后在crontab中每天定时执行脚本进行备份. 备份脚本示例: #!/bin/bash #Author: zhangsa ...

  5. 我手机上常用的app和常访问的网站

    ====常用======Opera Mini browser 浏览器(版本26.0.2254.117241以上) 老版本7.7最最经典, 但该版本在新的安卓手机上总有部分区域显示空白. 现在的 Ope ...

  6. DataTabe使用Linq实现 Group

    DataTable dt = dataSet.Tables[]; var query = from t in dt.AsEnumerable() group t by new { t1 = t.Fie ...

  7. js中文编码到C#后台解码

    escape() 方法: 采用ISO Latin字符集对指定的字符串进行编码.所有的空格符.标点符号.特殊字符以及其他非ASCII字符都将被转化成%xx格式的字符编码(xx等于该字符在字符集表里面的编 ...

  8. url 组成部分

    NSURL其实就是我们在浏览器上看到的网站地址,这不就是一个字符串么,为什么还要在写一个NSURL呢,主要是因为网站地址的字符串都比较复杂,包括很多请求参数,这样在请求过程中需要解析出来每个部门,所以 ...

  9. [译]Domain Events Pattern Example

    原文 完整源码 本文展示的是一个关于网上调查的项目.想象下,当用户完成了一个调查,我们想通知所有人调查已经结束,分配一个人去检查调用问卷. 领域对象 public class Survey { pub ...

  10. Javascript面向对象特性实现封装、继承、接口详细案例

    Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...