CDQ 分治学习笔记

CDQ 分治的流程大致是将对于区间 \([l,r]\) 中点 \(x,y\) 的计数分为两类处理:

  1. \(x,y\) 均位于 \([l,mid]\)\([mid+1,r]\) 中,这样的点对贡献可以递归解决。

  2. \(x,y\) 分别位于 \([l,mid]\)\([mid+1,r]\) 中,这样的点对通过一些操作来统计贡献。

显然这两类贡献之和即为 \([l,r]\) 的贡献。

三维偏序

算法流程

首先看一下模板题的削弱版:

有 $ n $ 个元素,第 $ i $ 个元素有 $ a_i,b_i,c_i $ 三个属性,满足所有 \(a_i\) 互不相同,所有 \(b_i\) 互不相同,所有 \(c_i\) 互不相同。设 $ f(i) $ 表示满足 $ a_j < a_i $ 且 $ b_j < b_i $ 且 $ c_j < c_i $ 且 $ j \ne i $ 的 \(j\) 的数量,对于 \(i \in [1,n]\),求出 \(f(i)\)

下面以此题为例讲解 CDQ 分治的流程。

第一步,对于整个数组以 \(a_i\) 从小到大排序。

第二步,进行 CDQ 分治,递归处理 \(1\) 类贡献,下面计算 \(2\) 类贡献:设当前区间为 \([l,r]\),中点为 \(mid\)。由于上一步已经对 \(a\) 一维排序过了,这意味着对于区间內部有且仅有 \(i \in [l,mid]\) 会对 \(j \in [mid+1,r]\)\(f\) 值产生贡献。再分别\([l,mid]\)\([mid+1,r]\) 两个区间按照 \(b_i\) 为关键字从小到大排序,这样对于 \(j \in [mid+1,r]\),能对其产生贡献的 \(i\) 一定是 \([l,mid]\) 的一个前缀,将这个前缀的 \(c_i\) 插入树状数组即可查询满足 \(c_j\) 限制的点的个数。显然若按照 \(j\) 的大小从小到大处理,区间内每个 \(i\) 只需要插入一次树状数组。

复杂度分析

设对于长度为 \(n\) 的区间进行 CDQ 分治的时间复杂度为 \(T(n)\),那么有递推式:

\[T(n) = 2T(\dfrac{n}{2}) + O(n \log n) \]

根据 \(\text{Master}\) 定理,对于形如 \(T(n) = a T(\dfrac{n}{b}) + f(n)\) 的时间复杂度递推式,设 \(c=\log_b a\),若存在非负数 \(k\) 满足 \(f(n) = n^c \cdot \log^{k}n\),则 \(T(n) = n^c \cdot \log^{k+1}n\)

观察上面的式子,\(a=2,b=2,c=1,k=1\) 时成立,那么 \(T(n)=n \log^2 n\),总复杂度就是 \(O(n \log^2 n)\)

P3810 【模板】三维偏序(陌上花开)

与上面一道题的区别在于存在了相等权值。对于两个数对若完全一样,那么就离散化为 \(1\) 个,统计时直接加上个数而不是加 \(1\),在排序时要按 \(a\) 为第一关键字,\(b\) 为第二关键字,\(c\) 为第三关键字,这样能保证一定是左面对右面有贡献。

P3157 [CQOI2011] 动态逆序对

给每个点打上时间戳 \(tim_i\),对于每个点,删去他减少的逆序对数目是他与前面的未删去点构成的逆序对数目(即\(tim_j > tim_i , pos_j < pos_i , val_j > val_i\) 的点对数目),以及后面未删去的点和该点构成的逆序对数目(即\(tim_j > tim_i , pos_j > pos_i , val_j < val_i\) 的点对数目),跑两次 CDQ 分别求这两类即可。

P2487 [SDOI2011] 拦截导弹

普通方程:

\[f_i = \max\limits_{j<i,h_i<h_j,v_i<v_j}\left\{f_j+1\right\} \]

观察到转移条件为三维偏序形式,那么 CDQ 一下,用树状数组维护一下最大值就好了。注意这里一定是先计算 \([mid+1,r]\)\(f\) 值再进行 \(CDQ(mid+1,r)\),这样才能保证计算右区间时左区间的 \(f\) 一定都计算完毕。

CDQ 分治维护斜率优化

P4655 [CEOI2017] Building Bridges

斜率式子很好写,但是会发现本题 \(k\)\(x\) 都是乱序的。考虑使用 CDQ 分治将其变为有序的来转移。

对于区间 \([l,r]\),显然 \([mid+1,r]\)\(f\) 可以从 \([l,mid]\) 转移而来,但是 \([l,mid]\) 一定不能从 \([mid+1,r]\) 转移,那就直接对于整个 \([l,mid]\) 按照 \(x\) 排序后将所有点插入凸包,对于 \([mid+1,r]\),按照 \(k\) 值排序后,发现 \(x\)\(k\) 均有序直接使用单调队列转移就行了。同样一定是先计算 \([mid+1,r]\)\(f\) 值再进行 \(CDQ(mid+1,r)\)

posted @ 2024-07-17 15:37  Aurora_Borealis  阅读(7)  评论(0编辑  收藏  举报