前言
有时候,题目给我们的树的编号是没有规律的。
此时,我们需要变化节点的编号,才能完成一些询问。
比如,我们熟悉的 序,它先将整棵树 一遍,记录下每个节点的时间戳;我们熟悉的树剖,它用了一种特殊的方式遍历了整棵树,即,对于任何一个节点都先遍历它的重孩子。这两种算法的精髓在于,我们把一些编号不连续的需要被操作或询问的节点,转化为了一些编号连续的节点或分成了许多不同的段,其中每个段内都是编号连续的节点。
这篇文章, 将以上述算法精髓为起点,介绍一下其他的节点编号方法,希望能够给大家一些帮助!
bfs序
例题
给定一棵树,需要支持下面两种操作:
表示在以 为根的子树中,所有深度为 的点权全部加 ;
表示在以 为根的子树中,所有深度为 的节点权值之和。
解法
读完本题后,可以发现 序与树链剖分均不适用。我们考虑bfs序。
序有一个重要的特点: 深度相同的一行的 序连续。
于是,我们先对整棵树进行一次 ,对于一次形如 的操作,我们可以找到在以 为根的子树中深度为 的点的节点编号的左端点与右端点;此时我们操作或询问的是一段连续的区间,我们就可以使用线段树来维护啦。
但是,我们如何找到左端点 与右端点 呢?
显然, 区间内的数都是要被操作或询问的,其他的均不是。于是,我们直接二分查询 与 的值即可。更详细地解释一下求 的值的步骤,我们对于该层所有节点进行二分,如果当前的 的祖先中有 ,或 的与 深度相同的祖先在 的右边,那么我们就向左找;否则向右找。求 的方法同理。
注意,我们这里找 的祖先中与 同深度的祖先,可以使用预处理+倍增来求出。
综上所述,我们使用二分套倍增得到了 的值,接着使用线段树进行修改或查询即可。
总时间复杂度为 。
中序
例题
给定一棵满二叉树。定义 表示以 为根的子树中节点的数量。对于每个满足以 为根的子树是对称二叉树的 ,求出 的最大值。
解法
如果我们直接深搜+减枝的话,时间复杂度为 ,无法通过本题。
可以发现,一棵二叉树对称,当且仅当这个二叉树的中序遍历是回文串。于是,我们可以找到原树的中序遍历,其中最长的一个回文串就是答案,显然可以用马拉车搞。
总时间复杂度 。