关于树上求 K-son 和 K-father 的各种方法

本篇灵感由翻阅 P5384 [Cnoi2019]雪松果树 题解区时得出,发现将这两种问题结合起来解决 K-cousin 问题竟然可以产生 \(3\times 6=18\) 种做法!特此记录。

注意,本篇记录的都是较为常见且优秀的正解算法,暴力算法并不被计算在内。


1. K-father

求 K 级祖先的问题十分常见,一般都会使用倍增进行处理,但其实还有复杂度较为优秀的长链剖分。若是不强调在线,甚至还有复杂度更加优秀的栈求 K 级祖先做法。


1.1 倍增

最常见的一种方法。考虑先 dfs 一遍整棵树,预处理出树上每个节点的二次幂级祖先,每次求该节点的祖先时由高次幂到低次幂依次向上跳,即可得到节点的 K-father。

时间复杂度 \(O(n\text{ log }n)-O(\text{log }n)\),空间复杂度 \(O(n\text{ log }n)\)在线算法


1.2 长链剖分

详细算法流程见 长链剖分 学习笔记

时间复杂度 \(O(n\text{ log }n)-O(1)\),空间复杂度 \(O(n\text{ log }n)\)在线算法


1.3 栈求 K 级祖先

很好懂,但不太好想的一种做法。

考虑通过一个栈来维护当前节点的所有祖先,从根节点一次向下遍历的时候顺带维护这个栈即可。先将所有询问读入后挂到节点上,然后遍历到该节点时,由于已经知道了该节点的所有祖先,故可以直接得出答案。

时间复杂度 \(O(n)-O(1)\),空间复杂度 \(O(n)\)离线算法


2. K-son


2.1 线段树合并

板子题啦!考虑利用权值线段树维护深度,表示以当前节点为根的子树内每一个深度的节点个数,然后套用线段树合并的板子,从下到上依次合并即可。

但是注意线段树合并有两种写法(这么说来求 K-son 是不是有 \(7\) 种方法/se),最常用的一种是合并完后立马回收子树节点,离线处理,这种的时间复杂度为 \(O(n\text{ log }n)-O(\text{log }n)\),空间复杂度为 \(O(n)\)

还有一种不太常见的写法,就是每次合并的时候新开节点,不删子树节点,好处是可以将离线变成在线,坏处是空间复杂度将暴涨到 \(O(n\text{ log }n)\),且 \(\text{log}\) 的常数巨大,一般会被卡掉。


2.2 树上差分

是不是树上差分不知道,反正是差分

线段树合并太浪费了!每次明明只需要 \(O(1)\) 查询,它非要建成一棵树,ban 掉!

考虑首先还是将所有的询问挂到树上,然后遍历整棵树,用一个数组维护当前每个深度的节点个数,遍历到一个节点时记录每个询问当前的答案,然后将该节点的深度加进贡献,遍历出来的时候再找一遍答案,两者的差值即为以当前节点为根的子树内的答案。

非常妙的方法。

时间复杂度 \(O(n)-O(1)\),空间复杂度 \(O(n)\)离线算法


2.3 Dsu on tree(树上启发式合并)

基本也算是板子题。

依旧是先将询问挂树上,然后使用合并套路,先剖出每个节点的轻重边,对于每个节点,先算出轻儿子子树内所有询问的答案,然后算重儿子子树内所有询问的答案,并继承重儿子的记录每个深度有多少个节点的数组,再暴力算出所有轻儿子子树对当前节点答案的贡献即可。

由于每个节点最多只会被记录 \(O(\text{log }n)\) 次,所以时间复杂度 \(O(n\text{ log }n)-O(1)\);由于全程只用到了一个记录深度的数组,所以空间复杂度 \(O(n)\)离线算法


2.4 DFS 序 + 树状数组

遇见子树统计问题,转 dfs 序!

考虑将原树转为一个 dfs序,那么对于每个节点 \(i\),它的子树区间就是 \([dfn_ i,dfn_i+size_i)\),将序列用每个节点的深度填充,则每个询问其实就是在查询某段区间内某个值的个数,变成了一道经典的二维数点问题。于是就可以使用套路,将所有询问挂树上之后按照 dfs 序依次处理每个节点的答案,就可以解决本问题了。

时间复杂度 \(O(n\text{ log }n)-O(\text{log }n)\),空间复杂度 \(O(n)\)离线算法

或许可以通过树套树转为在线算法?最多只是有亿点麻烦而已。


2.5 vector + 二分

考虑将每个深度建立一个 vector,然后将所有节点按照 dfs 序插入到对应的 vector 中。和上一种方法的原理差不多,由于转为 dfs 序后一个子树内的节点是连续的一段,所以就可以对于每个询问二分查找在询问深度的 vector 中,有多少个在当前的子树内,即可解决本题。

时间复杂度 \(O(n)-O(\text{log }n)\),空间复杂度 \(O(n)\)在线算法


2.6 长链剖分 + dp

详细算法流程见 长链剖分 学习笔记

时间复杂度 \(O(n)-O(1)\),空间复杂度 \(O(n)\)离线算法


搭配组合,\(18+\) 种解决 K-cousin 问题的方法你值得拥有!

posted @ 2022-05-16 11:27  ydtz  阅读(210)  评论(0编辑  收藏  举报