一、关于 cdq 分治
有的时候,后面的值会依赖前面的值。比如 DP 的过程中,要求出后面的 DP 值,我们就需要先求出前面的 DP 值。
这时候就需要用到 cdq 分治。这个分治的思路如下:
- 递归处理左边
- 计算左边对右边带来的贡献
- 递归处理右边
二、多维偏序问题
1. 最长上升子序列(二维偏序)
Solution:
设 f[i] 表示以 i 结尾的最长上升子序列的长度。
然后可以用 DP 来求解:f[i]=minj<i&aj<ai f[j]+1
直接做时间复杂度是 O(n2) 的。
考虑优化这个算法。
设当前分治区间是 [l,r],我们递归求完了 [l,m] 的答案,接下来要处理这一段对 [m+1,r] 的贡献。
把位于 [l,m] 的点看做插入,[m+1,r] 的点看做查询, 然后把整个区间按照 ai 排序。问题转化为支持插入 fi 和查询当前所有 f 的最大值。
只需要记录一个前缀最大值就可以了。
时间复杂度:O(n log n)。
可以不用 cdq 分治。我们先把 ai 离散化,然后开一个树状数组。
只需要实现单点修改和前缀查询最大值。时间复杂度还是 O(n log n)。
2. 三维偏序
题目大意:有 n 个元素,每一个元素有一个 ai 和 bi,求两个值都单调的最长上升子序列。1≤n≤105。
Solution:
还是先考虑 n2 算法:f[i]=minj<i&aj<ai&bj<bi f[j]+1
然后 cdq 分治。
我们把 [l,r] 内的元素按照 ai 排序。接下来问题转化为支持插入一个 bi 以及询问所有满足 bj < bi 的 fj 的最大值。
用树状数组解决。总时间复杂度 O(n log2 n)。
3. 四维偏序
题目大意:现在每一个值有三个属性 ai,bi,ci,还是求最长上升子序列。1≤n≤50000。
先按照 ai 排序,cdq 分治后分治区间内转化为三维偏序问题。
然后再 cdq 一次,内层用树状数组实现。
时间复杂度:O(n log3 n)
三、总结
如果我们有 k 个限制,那么 cdq 一次之后就可以去掉其中的一个限制,其中时间复杂度多一个 log。
不过 log4 及以上的复杂度就已经不如 O(n2) 算法快了,所以在实际应用中最多就是 cdq 套 cdq。
绝大多数 cdq 分治的题目都可以转化为 k 维偏序(或者 k 维数点)。