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


  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. 鼠标右键Table的td弹出多级菜单,双击td编辑

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...

  2. 51Nod - 1228 序列求和 (自然数幂和+伯努利数)

    https://vjudge.net/problem/51Nod-1228 Description T(n) = n^k,S(n) = T(1) + T(2) + ...... T(n).给出n和k, ...

  3. Mongoose笔记

    Mongoose是在node.js异步环境下对mongodb进行便捷操作的对象模型工具 mongoose是针对mongoDB操作的一个对象模型库,封装了mongoDB对文档的.增删改查等方法 使用Ko ...

  4. Docker-01 无人值守升级 CentOS 6.x 系统内核到 3.10.x 长期支持版

    #!/bin/bash # # 无人值守升级 CentOS .x 系统内核到 3.10.x 长期支持版 # # # .检查操作系统是否为 CentOS .x # cat /etc/centos-rel ...

  5. mysql中间件

    一.分类为:负载均衡类和数据切分类 1.负载均衡中间件提供了请求的转发,降低了单节点的负载,如haproxy,mysql-proxy,mysql-router 2.数据切分中间件按照不同的路由算法分发 ...

  6. “无法找到XXX.exe的调试信息,或调试信息不匹配”解决方案

    错误信息如下: 解决方法: 选择项目属性,依次序进行如下操作. 1.选择 配置属性->链接器->调试->生成调试信息 改为 是 一般问题都是出现在这个地方,修改完了可以尝试运行,若还 ...

  7. CentOS7查询最近修改的文件

    当需要排查问题的时候,经常需要找到最近修改和产生的文件 下面的命令是查询当前目录下以log结尾的日志,并且在30分钟内修改过,这个可以根据情况修改时间为1分钟,查找最新产生的日志 突然想到这个问题,是 ...

  8. 集成JUnit测试错误java.lang.IllegalStateException: Failed to load ApplicationContext

    1 详细错误信息 java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.t ...

  9. fastclick.js插件使用简单说明

    为什么存在延迟? 从点击屏幕上的元素到触发元素的 click 事件,移动浏览器会有大约 300 毫秒的等待时间.为什么这么设计呢? 因为它想看看你是不是要进行双击(double tap)操作.     ...

  10. scrapy基本使用(一)

    scrapy基本使用(一) 参考文档:Scrapy入门教程 http://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html scrapy ...