用线段树维护树的直径

目的

  有时候我们需要快速回答一棵子树的直径,或者去掉一棵子树后形成的树的直径。普通的找直径方法是两遍 bfs,时间 O ( n ) O(n) O(n),这里的方法用 O ( log ⁡ n ) O(\log n) O(logn) 的时间回答。

操作

首先

  我们做出一棵树的 dfs 序,然后以 dfs 序为轴建立线段树,每个区间维护直径 l e n len len,以及直径的两个端点 x x x y y y
  会有这么一个问题:你按线段树划分区间,那一个区间内的点可能不连通啊!!!其实没问题的,因为我们最后询问的是一棵(子)树,意思是我们询问的东西肯定是连通的。所以操作时候不联通你就当它连通就好啦。
  还会有这么一个问题:直径可能不止一条啊!!!随便记录一条即可,下文解释。

最关键是这个合并

  区间内只有 1 个点的时候 l e n = 0 len=0 len=0 x , y x,y x,y 等于自己。
  现在考虑区间的合并。左区间有两个直径端点,右区间也有两个直径端点,这四个点中的最远点对,就是新区间的直径。
  注意我们这里用的距离是原树的距离(已经说过我们强行让一个区间的点是连通的了)。

  简单证明一下。
  我们看作是 x x x 所在的连通块通过边 ( x , y ) (x,y) (x,y) 连向 y y y 所在的连通块。
  若新直径不经过 ( x , y ) (x,y) (x,y) ,则就是原来的两条直径取 max ⁡ \max max
  若新直径经过 ( x , y ) (x,y) (x,y) ,就要考虑 x x x 延伸到哪儿、 y y y 延伸到哪儿了。由直径的定义可知, x x x 能走到的最远点之一是 x x x 所在连通块直径的端点, y y y 同理。因此这时新直径的两个端点都是旧直径的端点。

  (这个证明也适用于增量法求树的直径,即我给一棵树加一个新点,那么新直径必有一端点是旧直径的端点)
  (注意只能是树,普通图没有这些性质)

所以

  有了这个神奇的合并操作,我们就可以求树中任意一个联通块(子树或是去掉一个子树什么的)的直径啦!

注意事项

  我们求点对距离时会用到 lca,然而线段树已经有一个 log ⁡ \log log 了,如果碰到题目要卡两个 log ⁡ \log log,就要用 rmq 求 lca。

例题

例题1
例题2

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Dijkstra算法是一种用于解决单源最短路径问题的经典算法,它的时间复杂度为O(V^2),其中V是图中顶点的数量。通过使用线段树优化Dijkstra算法,可以将其时间复杂度降低到O((V+E)logV),其中E是图中边的数量。 下面是使用线段树优化Dijkstra算法的一般步骤: 1. 创建一个大小为V的距离数组dist[],用于记录从源节点到每个节点的当前最短距离,初始时将所有距离初始化为无限大,源节点的距离初始化为0。 2. 创建一个大小为V的布尔数组visited[],用于记录每个节点是否已被访问过,初始时将所有节点标记为未访问。 3. 创建一个线段树,用于快速查询未访问节点中距离最小的节点。 4. 将源节点加入线段树,并将其距离设置为0。 5. 重复以下步骤,直到线段树为空: - 从线段树中选择距离最小的节点u,并将其从线段树中删除。 - 标记节点u为已访问。 - 遍历节点u的所有邻居节点v: - 如果节点v未被访问过且通过节点u到达节点v的距离更短,则更新节点v的距离为新的最短距离,并将节点v插入线段树中。 6. 最终,dist[]数组中存储的即为从源节点到每个节点的最短距离。 通过使用线段树优化Dijkstra算法,可以减少在每次查找最小距离节点时的时间复杂度,从而提高算法的效率。需要注意的是,在实现过程中,还需要根据具体问题进行相应的调整和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值