节点编号二三事

前言

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

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

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

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

bfs序

例题

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

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

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

解法

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

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

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

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

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

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

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

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

中序

例题

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

n ≤ 1 0 7 n≤10^7 n107

解法

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

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

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值