[持续更新] 杂谈点分治 点分树 边分治 链分治

个人主观题目难度评级
\(\mathtt{A:}\) 思维难度大 / 实现难的题目
\(\mathtt{B:}\) 有思维难度 / 实现有一定难度的题目
\(\mathtt{C:}\) 朴素的题目
\(\mathtt{^x:}\) 自己独立解出的题目
量化评级:\(\mathtt{0\sim10}\)

时间有限刚写完,只有一些水题
以后还会添加一些难度较大的题目
以下默认数据范围 \(1\times 10^5\) 级别

$$\huge\textbf{0x01}\ \large\textbf{点分治}$$

对于树上的每个节点来说
树上所有的路径分为两种
经过该点和不经过该点的(被打
这就是点分治的大致思想
假设要解决树上路径问题
那我们先从树上拎一个点
统计经过该点的路径信息
然后就把该点从树上删去
根据上面给出的大致思想
剩下的路径都不经过该点
同时树会被分成若干子树
对子树进行同样步骤即可
直至把树上所有点全删掉
根据上面给出的大致思想
所有的路径都已被统计过
当然这个点不能随便选取
为了删空点所需次数最少
进而保证时间复杂度正确
删的是当前每棵树的重心

点分治<双指针> \(\mathtt{[C|7.3]}\)

给定一棵有 \(n\) 个点的树,询问树上距离为 \(k\) 的点对是否存在。
点分治流程我们已经了解
考虑统计树根答案的方法
把该点所在的树遍历一遍
\(d\) 表示该点到树根的距离
\(s\) 表示该点所在子树编号
先把节点按照 \(d\) 大小排序
要的是不同子树中两个点
两点 \(d\) 的和恰好就等于 \(k\)
与此同时两点的 \(s\) 不相同
可以使用双指针进行查找
不能遍历已经删去的节点

点分治<桶> \(\mathtt{[C|7.0]}\)

给定一棵有 \(n\) 个点的树,询问树上距离为 \(k\) 的点对是否存在。
考虑统计树根答案的方法
开大小为 \(k\)的桶统计路径
每个下标存储该点子树中
有无长度为该下标的路径
对于每一棵子树依次遍历
这些路径同桶内数据合并
再将这些路径放进桶里面
合并的路径必属不同子树
注意预先将树根放入桶内
View Code

Race \(\mathtt{[C^x|7.2]}\)

给一棵树,每条边有权。求一条简单路径,权值和等于 \(k\),且边的数量最小。
和上一道题基本一模一样
桶储存的数据类型为 int
即长为下标的所有路径中
路径所含的边数的最小值
View Code

Tree \(\mathtt{[C^x|7.6]}\)

给定一棵 \(n\) 个节点的树,每条边有边权,求出树上两点距离小于等于 \(k\) 的点对数量。
查询从查询单点变成前缀
树状数组维护桶数组即可
View Code

最长道路Tree \(\mathtt{[C^x|7.8]}\)

给定一棵 \(n\) 个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大。
桶储存链最小值为下标时
对应所有链长度的最大值
要查询该点为起点的后缀最大值
反向维护树状数组来查询

Freezing With Style \(\mathtt{[A|8.5]}\)

树,求边数在 \([L,R]\) 之间的路径,使路径边权的中位数最大。
中位数一眼丁真二分答案
边权 \(≥ mid\)\(1\) 反之 \(-1\)
找树中有无链权和大于 \(0\)
关键是本题链边数的限制
考虑把桶换成权值线段树
单点修改区间查询最大值
复杂度 \(3\log\) 还不够优秀
如果我们 bfs 完整个子树
从根出发到达子树中一点
需要经过的边数只增不减
而且为了和某段路径合并
需查询最大值的区间长不变
所以某棵子树 bfs 过程中
可以用单调队列从头到尾
查询桶数组某一段最大值
再将整棵子树全放入桶内
这样复杂度似乎已经对了
但是单调队列进行初始化
的次数是该点子树的数量
每次初始化从 \(L\) 走到 \(R\)
时间复杂度最坏可能 \(n^2\)
造个菊花就能拿捏这东西
每次初始化可以不走到 \(R\)
走到之前子树的最大深度
但链底下挂菊花可以卡掉
需用到单调队列按秩合并
子树按深度从小到大排序
然后复杂度就变成对的了
初始化复杂度一定不超过
以该点为根的子树的大小
点分治复杂度 \(O(n\log n)\)
总的复杂度 \(O(n\log^2 n)\)

$$\huge\textbf{0x02}\ \large\textbf{点分树}$$

点分树是跟点分治有关的树(被打
把点分治经过的点离线下
在点分树上一个点的儿子
是原树点分治中删去它后
该点所在的树
分裂成的所有子树的重心
点分树深度和点分治相同
取重心子树大小至少减半
深度 \(\log n\) 非常平衡
支持原树无法完成的暴力
比如暴力跳父亲这种操作

Unnamed \(\mathtt{[C|7.5]}\)

树,边带权,\(q\) 次询问,求距离某个点不超过 \(k\) 的点有多少个
点分树有一个重要的性质
任意两点的最近公共祖先
一定在原树中两点路径上

我们可以方便地分别处理
两点和它们点分树上 LCA
之间在原树的的路径信息
然后进行合并 以这道题为例
枚举查询点与答案点分树上的 LCA
然后对于每一个枚举的点
计算它点分树上的子树中
扣掉查询点所在的子树后
剩余部分的答案
用数据结构维护一下就行(绝对不是我写不动了

$$\huge\textbf{0x03}\ \large\textbf{边分治}$$

边分治和点分治大致相同
只是分治对象从点变成边
对于菊花无法找合适的边
时间复杂度退化为 \(O(n^2)\)
需要 rebuild 变成二叉树
将儿子和一个虚点串起来
再将虚点间连边串成一串
个人感觉没有点分治简便
(如有特殊应用尽管D我

$$\huge\textbf{0x04}\ \large\textbf{链分治}$$

针对树上的链进行的分治
它还有个别名叫树链剖分
博客另一篇文章中有叙述
(尽管还没发布…………
To Be Continued

posted @ 2023-04-29 20:51  ReTF  阅读(86)  评论(1编辑  收藏  举报