树的直径与最近公共祖先(LCA)

树的直径

这是树型结构中的一个重要概念,可以用于解决许多问题。树的直径有两种求法:一是通过2次bfs求得,二是通过树形dp求得。bfs的优点是可以方便地求出直径上的点,dp的优点是代码短且可以求带有负权边的树的直径。
bfs法:
1.以任意一个节点为起点,求出与它最远的节点p。
2.以p为起点,再做一次bfs,求出与它最远的节点q
3.p,q间的路径即为树的直径
证明略复杂,情况较多,只要大致了解并记忆这个做法就可以了。
树形dp法
一个很巧妙的办法:首先很容易考虑树形dp用dx表示从x出发可以走到以x为根的子树中的节点的最远距离,那么不妨设x的子节点为y1,y2,y3,……,yt
显然dx=max(dyi+edge(x,yi)) (i ∈ \in [1,t])。而经过x的最长链长度fx=max(dyi+edge(x,yi)+dyj+edge(x,yj)) (1 ≤ \leq j<i ≤ \leq t)。显然去枚举并不是明智之举,我们发现当dx尚未被更新时,恰好储存的就是
max(dyj+edge(x,yj)) (i ∈ \in [1,i-1])的值,那么显然fx可表示为fx=max(dx+dyi+edge(x,yi)),然后再去更新一下dx就可以了。
总结一下就是:妙用已经维护了的信息。(物尽其用)

例题1:AcWing 350.巡逻

这个题目是一个很好的练习题,考察了两种树的直径的求法。那么初步分析这个题:不妨把地图变成一个以1为根节点的树,那么不加任何一条边的走法实际上就是树的遍历,最小巡逻距离就是2(n-1)。
先考虑K=1的情况:我们要使巡逻的总距离尽量短,就要让中间可以尽量多节省一些距离。那么我们很容易考虑到:加入一条路径后,直接通过它跳过一段路径,直接进行后一段回溯就可以使答案变小了。那么我们就要找到连接两点的最长路径:树的直径。把这条边直接加在树的直径的端点之间即可。那么最终的答案就是2(n-1)-L1+1。(L1为树的直径)
再考虑K=2的情况:接下来的思路毫无疑问是要找另一条直径并把它的长度减去。但是事实真的这么简单吗?实际上这样找的直径两端相连后成的两个环有可能会重叠,那么实际上重叠部分不会被走过,那么这是不合题意的,实际上重叠部分又变回了走两次的状态。那么怎么修改呢?其实很简单:将第一条直径上的所有边权置为-1(这样算答案的时候就又会把次数加回来),然后再求一次树的直径即可,记为L2。那么最终的答案就是2(n-1)-L1+1-L2+1=2n-L1-L2。如果L2是包含负权边的,那么-L2相当于加回了L1中减去的次数。

最近公共祖先(LCA)

求最近公共祖先的两种方法:

倍增DP法

void bfs(int root){
   
    queue<int> q;
    q.push(root);
    d[root]=1;
    while(q.size()){
   
        int t=q.front();
        q.pop();
        for(int i=head[t];i;i=ne[i]){
   
            int j=to[i];
            if(d[j]) continue;
            dist[j]=dist[t]+w[i];
            d[j
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值