CDQ 分治是一种离线的分治思想,可以用来处理以下问题:
- 解决和点对有关的问题。
- 1D 动态规划的优化与转移。
- 通过 CDQ 分治,将一些动态问题转化为静态问题。
(摘自 oiwiki)
解决和点对有关的问题
算法流程如下:
要解决 的问题,先将 的问题解决,再解决整个区间的问题,所以不严格得讲,分治求平面最近点对、归并排序都是 CDQ 分治的思想(但是 CDQ 分治一般会套上数据结构)。
我们要求 中的点对 ,先分治,解决 的子问题,再考虑 的点对,我们先在一开始让数组按 升序排序,保证 ,然后再左右分别按 升序排序,这是为了方便我们双指针找到所有满足 的 ,接下来就不用多说了,用树状数组把 存起来然后计算即可。
Code
注意:cmp1 不仅要按 排,还要在 相同时按 排,否则可能会让满足条件的点对 在右边而统计不到, cmp2 则不用(但建议还是写一下)
记 为 被删除的时间,如果没有被删除则 ,然后考虑一个点 删除前后,删除后少去了其本身当前的贡献,也就是点对 满足 ,这就转化成了一个三维偏序问题。
Code
优化动态规划的转移
其实和点对之间的关系的处理是同理的,但是要注意的是,处理跨越区间 ()的问题时,要按照正确的 dp 转移顺序来。例如,我们有一个二维的 LIS 问题,转移方程为:
这里我们之间一个 cdq 分治上去,树状数组维护前缀最大值就能优化到 , 但是中间的处理部分必须要放在 cdq(l, mid) 和 cdq(mid+1, r) 之间,因为在转移 的时候要求 的 都被处理完了。如果 , 则说明 处理完毕了,直接 return 即可。
如上,第一问跑一个二维最长非升子序列即可,但是第二问,我们需要求出存在 的最长非升子序列个数,除以总个数,这里有个 trick,只要求出以 结尾、开头的最长非升子序列个数,再相乘即可(如果两者拼起来的长度为全局最长长度的话),那么目标明确,两边 就行,细节有点多,可以慢慢理解下代码。
Code
将动态问题转化为静态问题
这里,CDQ 分治折半的是 时间序列,也可以说是操作(修改和询问)顺序序列,常用于解决 修改 xxx 询问 xxx 的问题,但是这是个离线算法,我们要先把所有修改、询问存下来,这样操作本身就按时间排好序了,这时,每个修改都会影响到后面的询问,这样的点对共有 O(n^2) 对。称为一个修改-询问关系,下面我们用 来表示第 次操作。
接下来思路便明了了,我们在解决 中所有修改和询问问题时,先处理 之间的关系,再处理 的关系,其中 是一个修改, 是一个询问。
有一点要注意的是,如果修改之间相互 , 比如区间加减什么的,这时候我们随便什么时候处理
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!