0x00 前言
话说cdq应该大写还是小写。。。。。
0x01 什么是cdq分治?
设问题区间为[L,R],中点为MID
普通的分治:
- 递归处理[L,MID]
- 递归处理[MID+1,R]
- 合并答案
cdq分治:
- 递归处理[L,MID]
- 递归处理[MID+1,R]
- 处理[L,MID]区间对[MID+1,R]区间的影响
哇听起来好厉害,但是不懂是什么意思啊
0x02 你在哪里遇见过cdq分治?
其实几乎所有人都写过cdq分治
我们考虑一个问题
问题1
给定一个序列,序列中有一些元素,每个元素有两个权值ai,bi,问有多少对元素满足ai>aj,bi<bj
这是一个非常经典的问题(没听说过的话那还是死死掉比较好)
如果ai是1~n的排列的话那就是全裸的逆序对了
考虑用归并排序求解这个问题的过程
- 按ai排序
- 对bi归并
- 把一个区间分成左右两半
- 分别处理左右两半
- 每次我们需要从右半部分吐出一个元素时,这个元素和左半部分所有剩下的元素构成逆序对
这好像和cdq分治差不多啊?
每次从右半边吐出来一个元素时,统计左边剩下的所有元素对它的影响
恩很对
0x03 cdq分治的实质?
考虑一个新的问题
问题2
给定一个序列,序列中有一些元素,每个元素有两个权值ai,bi,问有多少对元素满足ai>aj,bi>bj
这个应该叫顺序对问题?
做法的话,把刚才求逆序对的方式改一下就行了,没说不能每次统计[MID+1,R]区间对[L,MID]区间的影响啊
对于这个问题,我们有一个看上去很傻疑问
- 问:为什么不能先按a排一遍序,再按b排一遍序,然后线性扫一遍得出答案?
- 答:因为对b排序的时候会破坏a的相对关系
然后我们考虑这样的分治做了什么
对[L,MID]和[MID+1,R]分别归并完后,[L,MID]和[MID+1,R]现在分别是关于b有序的了,但是关于a无序。精妙的是,[L,MID]和[MID+1,R]之间关于a是相对有序的。即,[L,MID]范围内的a都小于[MID+1,R]范围内的a。
妙啊!(拍桌子)
这样就可以直接统计了
0x04 拓展
显然会有下面这种问题
问题1
给定一个序列,序列中有一些元素,每个元素有三个权值ai,bi,ci,问有多少对元素满足ai>aj,bi>bj,ci>cj
这怎么搞?
按照刚才的思路,对a排序,对b cdq分治,c呢?
那就只好上一个二维数据结构了,每次扫描到[L,MID]区间内的ci时把ci插入树状数组(或者别的什么东西),扫描到[MID+1,R]区间内的ci时在树状数组里查询有多少cj小于它
完美解决
这里我们可以看到,cdq分治思想可以顶一层数据结构。不用cdq分治的话只好树套树了吧。。
0x05 邪教用法
刚才的例子里我们发现cdq分治可以顶一层数据结构,那如果只用一层数据结构的题呢?能不能不用数据结构?
比方说,老年人选手连树状数组都写不动怎么办?
考虑树状数组的经典问题,单点修改,询问区间和
树状数组做区间和查询的时候是拆成前缀和的形式做的,这里我们也拆成前缀和做
数组有初值?没关系,把所有初值改成修改,时间放在最前面。
然后像做逆序对那么搞就行了,ai是时间,bi是位置,统计一下[L,MID]区间内的修改的和,在[MID+1,R]区间内相应修改
我好懒啊,不想上代码。
上网找别的dalao的代码吧orz,写的比我好多了
0x06 杂物
cdq分治实际上把每个修改统计了logn次
cdq分治的时候你得写归并排序,或者你可以偷懒用sort,复杂度多一个log(这很致命)
cdq分治要求修改相互独立
cdq分治不能处理删除,有删除的话,你可能得倒过来做
cdq分治很短。。。比二层数据结构好写多了
今天的Yayoi依然因为线段树常数太大TLE了
0x07 例题
都是权限题orz
一般来说都是维护三维偏序关系
陌上花开(这好名字真是糟蹋了)
动态逆序对
mokia