CDQ 分治的流程大致是将对于区间 \([l,r]\) 中点 \(x,y\) 的计数分为两类处理:
-
\(x,y\) 均位于 \([l,mid]\) 或 \([mid+1,r]\) 中,这样的点对贡献可以递归解决。
-
\(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 分别求这两类即可。
标签:log,分治,mid,笔记,贡献,tim,CDQ From: https://www.cnblogs.com/victoryang-not-found/p/18307301