节点编号二三事

前言

有时候,题目给我们的树的编号是没有规律的。

此时,我们需要变化节点的编号,才能完成一些询问。

比如,我们熟悉的 d f s dfs 序,它先将整棵树 d f s dfs 一遍,记录下每个节点的时间戳;我们熟悉的树剖,它用了一种特殊的方式遍历了整棵树,即,对于任何一个节点都先遍历它的重孩子。这两种算法的精髓在于,我们把一些编号不连续的需要被操作或询问的节点,转化为了一些编号连续的节点或分成了许多不同的段,其中每个段内都是编号连续的节点。

这篇文章, 将以上述算法精髓为起点,介绍一下其他的节点编号方法,希望能够给大家一些帮助!

bfs序

例题

给定一棵树,需要支持下面两种操作:

1   u   d   k 1\ u\ d\ k 表示在以 u u 为根的子树中,所有深度为 d d 的点权全部加 k k

2   u   d 2\ u\ d 表示在以 u u 为根的子树中,所有深度为 d d 的节点权值之和。

解法

读完本题后,可以发现 d f s dfs 序与树链剖分均不适用。我们考虑bfs序

b f s bfs 序有一个重要的特点: 深度相同的一行的 b f s bfs 序连续。

于是,我们先对整棵树进行一次 b f s bfs ,对于一次形如 1   u   d   k 1\ u\ d\ k 的操作,我们可以找到在以 u u 为根的子树中深度为 d d 的点的节点编号的左端点右端点;此时我们操作或询问的是一段连续的区间,我们就可以使用线段树来维护啦。

但是,我们如何找到左端点 l l 右端点 r r 呢?

显然, [ l , r ] [l,r] 区间内的数都是要被操作或询问的,其他的均不是。于是,我们直接二分查询 l l r r 的值即可。更详细地解释一下求 l l 的值的步骤,我们对于该层所有节点进行二分,如果当前的 m i d mid 的祖先中有 u u ,或 m i d mid 的与 u u 深度相同的祖先 u u 的右边,那么我们就向左找;否则向右找。求 r r 的方法同理。

注意,我们这里找 m i d mid 的祖先中与 u u 同深度的祖先,可以使用预处理+倍增来求出。

综上所述,我们使用二分套倍增得到了 l , r l,r 的值,接着使用线段树进行修改或查询即可。

总时间复杂度为 O ( n l o g n + q l o g 2 n ) O(nlogn+qlog^2n)

中序

例题

给定一棵满二叉树。定义 s ( i ) s(i) 表示以 i i 为根的子树中节点的数量。对于每个满足 i i 为根的子树是对称二叉树 i i ,求出 s ( i ) s(i) 的最大值。

n 1 0 7 n≤10^7

解法

如果我们直接深搜+减枝的话,时间复杂度为 O ( n l o g n ) O(nlogn) ,无法通过本题。

可以发现,一棵二叉树对称,当且仅当这个二叉树的中序遍历是回文串。于是,我们可以找到原树的中序遍历,其中最长的一个回文串就是答案,显然可以用马拉车搞。

总时间复杂度 O ( n ) O(n)

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/108326800