还记得你把所有的 $i$ 和 $j$ 都事先按照 $a$ 值排好序了吗?我们以双指针的方式在树状数组里插入点,这样的话我们就只需要做 $O(n)$ 次插入操作啦~
-所以通过这样一个算法流程我们就用 $O(nlogn)$ 的时间处理完了关于第 $2$ 类点对的信息了
+所以通过这样一个算法流程我们就用 $O(n\log n)$ 的时间处理完了关于第 $2$ 类点对的信息了
-这样的话我们的算法复杂度就是 $T(n)=T(\lfloor \frac{n}{2} \rfloor)+T(\lceil \frac{n}{2} \rceil)+O(nlogn)=O(nlog^2n)$ 了
+这样的话我们的算法复杂度就是 $T(n)=T(\lfloor \frac{n}{2} \rfloor)+T(\lceil \frac{n}{2} \rceil)+O(n\log n)=O(n\log^2n)$ 了
### 例题[CQOI2011]动态逆序对
## CDQ 分治优化 1D/1D 动态规划的转移
-所谓 1D/1D 动态规划就是说我们的 dp 数组是 1 维的,转移是 $O(n)$ 的一类 dp 问题,如果条件良好的话我们有些时候可以通过 cdq 分治来把这类问题的时间复杂度由 $O(n^2)$ 降至 $O(nlog^2n)$
+所谓 1D/1D 动态规划就是说我们的 dp 数组是 1 维的,转移是 $O(n)$ 的一类 dp 问题,如果条件良好的话我们有些时候可以通过 cdq 分治来把这类问题的时间复杂度由 $O(n^2)$ 降至 $O(n\log^2n)$
那么比如说我们要优化这样的一个 $dp$ 式子给你一个序列每个元素有两个属性 $a,b$ 我们希望计算一个 dp 式子的值,它的转移方程如下:
又比如说 $5$ 这个点它的 dp 值就是在 $solve(1,4)$ 函数中解决的,更新它的区间是 $(1,4)$
-仔细观察就会发现一个 i 点的 dp 值被更新了 $log$ 次,而且,更新它的区间刚好是 $(1,i)$ 在线段树上被拆分出来的 log 个区间
+仔细观察就会发现一个 i 点的 dp 值被更新了 log 次,而且,更新它的区间刚好是 $(1,i)$ 在线段树上被拆分出来的 log 个区间
因此我们的第 2 个条件就满足了,我们的确保证了所有合法的 $j$ 都去更新过点 $i$
这个问题就等价于平面上有静态的一堆矩形接下来不停的询问一个矩形区域的和了
-那么我们可以套一个扫描线在 $O(nlogn)$ 的时间内处理好所有跨越 $mid$ 的修改 - 询问关系
+那么我们可以套一个扫描线在 $O(n\log n)$ 的时间内处理好所有跨越 $mid$ 的修改 - 询问关系
剩下的事情就是递归的分治左右两侧修改 - 询问关系来解决这个问题了
-这样实现的 cdq 分治的话你会发现同一个询问被处理了 $O(logn)$ 次来回答,不过没有关系因为每次贡献这个询问的修改是互不相交的
+这样实现的 cdq 分治的话你会发现同一个询问被处理了 $O(\log n)$ 次来回答,不过没有关系因为每次贡献这个询问的修改是互不相交的
-时间复杂度为 $T(n)=T(\lfloor \frac{n}{2} \rfloor)+T(\lceil \frac{n}{2} \rceil)+ O(nlogn)=O(nlog^2n)$
+时间复杂度为 $T(n)=T(\lfloor \frac{n}{2} \rfloor)+T(\lceil \frac{n}{2} \rceil)+ O(n\log n)=O(n\log^2n)$
观察上述的算法流程,我们发现一开始我们只能解决静态的矩形加矩形求和问题,但是只是简单的套了一个 cdq 分治上去我们就可以离线的解决一个动态的矩形加矩形求和问题了。
所以我们就证明了每次我们用来跑 kruskal 的图都是 $O(len)$ 级别的了
-从而每一层的时间复杂度都是 $(nlogn)$ 了
+从而每一层的时间复杂度都是 $O(n\log n)$ 了
-因此我们的时间复杂度就是 $T(n)=T(\lfloor \frac{n}{2} \rfloor)+T(\lceil \frac{n}{2} \rceil)+ O(nlogn)=O(nlog^2n)$ 了
+因此我们的时间复杂度就是 $T(n)=T(\lfloor \frac{n}{2} \rfloor)+T(\lceil \frac{n}{2} \rceil)+ O(n\log n)=O(n\log^2n)$ 了
代码实现上可能会有一些难度,需要注意的是并查集不能使用路径压缩,否则就不支持回退操作了,执行缩点操作的时候也没有必要真的执行,而是每一层的 kruskal 都在上一层的并查集里直接做就可以了