树上问题

树上问题
风铃夜行树上问题
树的直径
1 | struct Tree { |
树论大封装(直径+重心+中心)
1 | struct Tree { |
点分治 / 树的重心
重心的定义:删除树上的某一个点,会得到若干棵子树;删除某点后,得到的最大子树最小,这个点称为重心。我们假设某个点是重心,记录此时最大子树的最小值,遍历完所有点后取最大值即可。
重心的性质:重心最多可能会有两个,且此时两个重心相邻。
点分治的一般过程是:取重心为新树的根,随后使用 child
和 pre
两个数组分别计算通过根节点、不通过根节点的路径信息,根据需要进行答案的更新;再对子树分治,寻找子树的重心,……。时间复杂度降至
1 | int root = 0, MaxTree = 1e18; //分别代表重心下标、最大子树大小 |
最近公共祖先 LCA
树链剖分解法
预处理时间复杂度
1 | struct HLD { |
树上倍增解法
预处理时间复杂度
封装一:基础封装,针对无权图。
1 | struct Tree { |
封装二:扩展封装,针对有权图,支持“倍增查询两点路径上的最大边权”功能。
1 | struct Tree { |
树上路径交
计算两条路径的交点数量,直接载入任意 LCA 封装即可。
1 | int intersection(int x, int y, int X, int Y) { |
树上启发式合并 (DSU on tree)
1 | struct HLD { |
prufur 序列
对树建立 Prüfer 序列
Prüfer 是这样建立的:每次选择一个编号最小的叶结点并删掉它,然后在序列中记录下它连接到的那个结点。重复
显然使用堆可以做到
1 | // 代码摘自原文,结点是从 0 标号的 |
1 | # 结点是从 0 标号的 |
Cayley 公式 (Cayley’s formula)
完全图
怎么证明?方法很多,但是用 Prüfer 序列证是很简单的。任意一个长度为
图连通方案数
Prüfer 序列可能比你想得还强大。它能创造比 凯莱公式 更通用的公式。比如以下问题:
一个
个点 条边的带标号无向图有 个连通块。我们希望添加 条边使得整个图连通。求方案数。
设
重链剖分
1 | struct HPD_tree |