树论
树论
LCA
树上公共祖先问题,树论中最基础的问题。
最常见的倍增法可以做到:\(O(n\log n)-O(\log n)\);
带树剖的题目随手写的树剖 LCA 复杂度为 \(O(n)-O(\log n)\),但效率非常好,自带 \(\dfrac 1 4\) 常数;
需要 \(O(1)\) 查询的情况可以实现一般的 \(O(n\log n)-O(1)\) 做法,欧拉序 ST 表即可,也有其他序可以做到相同复杂度;
四毛子做到的 \(O(n)-O(1)\) 在 OI 中太不实用。
树链剖分
树论里一大利器。
重剖好的性质是任意一个点到根的链只有 \(O(\log n)\) 条轻边,轻子树大小和是 \(O(n\log n)\) 量级(链分治所依赖的性质)。
长剖一个点到根的链轻边条数是 \(O(\sqrt n)\) 的,长剖可以优化一些状态数和子树深度同阶的树上 DP,另一个重要的应用是 \(O(n\log n)-O(1)\) 树上 \(k\) 级祖先。
一些复杂的问题我们需要用数据结构维护轻儿子支持查询邻域。
一个不错的例子是:[北大集训 2021] 经典游戏。
树分治
将分治思想在树上运用,我们可以得到点边链三种方向的树分治。
点分治可以将树上路径问题转化成过分治中心的两条路径。
如果统计的信息有可减性,我们可以先忽略统计信息需要在不同子树再容斥掉同一子树内的贡献。
如果统计的信息性质不大好,我们可以前后缀各扫一遍统计贡献。
点分治另外一个应用是加速寻找重心的过程,利用了点分树深度为 \(O(\log n)\) 的性质。
需要修改的点分治可以用到点分树,将点分治的过程记录下来就是点分树了。
边分治和点分治哈夫曼树合并等价。由于菊花图的存在,边分治需要三度化,笔者并没有实现过类似的东西。
边分治的优越性在于每次只会分成两个子问题,一些原本在点分治上复杂度不正确的东西在边分治中有了更多的可能性。
链分治见的不多,在 SAM 题里不少。例子有:Ж-function,Border 的四种求法。
dsu on tree 与线段树合并
正统的 dsu on tree 的空间复杂度应当是 \(O(n)\) 的,算法流程参考伪代码:
结构 S
void dfs(int x,bool op){
for(y 不是 x 的重儿子)dfs(y,0);
if(x 有重儿子)dfs(hson[x],1);
处理答案,将 x 的信息加入 S。
for(y 不是 x 的重儿子){
处理答案,将子树 y 内的信息加入 S。
}
if(!op)将 S 的信息清空。
return ;
}
线段树合并可以处理一些转移可以用线段树操作描述的 DP 问题,难以用其他方法替代。
虚树
核心思想是把关键点拿出来抽象出一个新的树结构,建立的方法与笛卡尔树相同均考虑最右链。
树同构
巨大冷门考点,22 年居然考了一次,拎出来提一提。
非常好博客,推荐阅读:link。
杂项小技巧
-
全集去掉某个点的子树以及其到根的路径的范围
这个范围当然可以使用树剖变成 \(O(\log n)\) 段区间,但是可以使用两个序做到每个序的一个前缀。
考虑利用后序遍历与反后序遍历(边表反转的后序遍历),范围是两个序分别一个前缀的并。 -
bdfs 序
一个很有用的同时兼顾邻域和子树的序,即同时具有 dfs 和 bfs 的性质。
bdfs 序具体是 dfs 遍历到一个点时,一次加入其所有孩子,继续 dfs 孩子。
它的性质也很简单:
- 一个点的所有儿子构成一段区间。
- 一个点的子树除去这个点自身外,构成一段区间。
配合重链剖分可以实现链与邻域上的操作。
具体的,bdfs 序(先 dfs 重儿子)里存在一个区间满足其内的非重链顶点均是一条重链上的点,我们可以将重链顶点(轻点)和非重链顶点(重点)分开维护。 -
链加,子树求和
显然可以树剖做 \(\log^2n\),但是这个也可以做单 \(\log\)。
考虑同时使用先序遍历与后序遍历,那么点到根的路径可以表示成先序遍历的一个前缀(包含 \(x\) 的子树)和后序遍历(不包含 \(x\) 的子树)的一个前缀做差。那么问题转化为前缀加,区间求和,显然可以做 \(O(n\log n)\)。
-
维护支持插入,撤销的子树补信息
-
快速寻找树上带权重心
记总权值和为 \(S\),子树 \(x\) 的权值和为 \(s_x\)。
考虑对于重心 \(h\),其满足 \(S-s_h\leq \dfrac S 2\),则 \(s_h\geq \dfrac S 2\)。我们考虑寻找最浅的重心,那么当 \(s_x=\dfrac S 2\) 时,我们容易发现其父亲也一定是重心。
则取 \(s_h>\dfrac S 2\),考虑 dfs 序区间的带权中心点,重心的子树区间一定包含它,倍增判断即可。
容易支持一系列修改权值操作。 -
一种经典的 xor-hashing
对于一个连通图建出其生成树,将非树边赋予 64 位随机权值,将树边赋予覆盖它的非树边的权值异或和。
对于一个边集 \(E\),删掉 \(E\) 后原图仍连通,等价于 \(E\) 中权值构成线性空间维数等于 \(|E|\)。