【Jeoy‘ s daily 】AcWing 1171. 距离 LCA Tarjan模板

该博客介绍了如何利用Tarjan算法解决树上两点间最短路径的问题。作者首先解释了Tarjan算法的基本思想,并展示了一个BFS搜索的例子。接着,详细阐述了如何在BFS过程中计算每个节点到根节点的距离,并利用这些信息快速计算两点间的最短距离。文章还给出了具体的代码实现和样例输入输出,以帮助读者理解算法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

古人云:倍增是做不好LCA的,只有tarjan才行…
原题链接
浅浅画了个图

tarjan思维就是,假设我现在在用bfs搜索,我搜到某一个点,假设这个点是某已搜过点(a)和某未搜过点(b)的 最小公共祖先,那么此时我们只需要预先处理一下每一个点(dist数组)和根节点之间的距离,即可计算两点之间的距离的时候,此时需要计算一个:

len ( a, b ) = dist [ a ] + dist [ b ] - 2 * dist[ LCA ];

y总在模板里存数据用的是vector,这个方法我确实是没学过,确实是学习了

====================================================
题目:

给出 n 个点的一棵树,多次询问两点之间的最短距离。

注意:

边是无向的。
所有节点的编号是 1,2,…,n。
输入格式
第一行为两个整数 n 和 m。n 表示点数,m 表示询问次数;

下来 n−1 行,每行三个整数 x,y,k,表示点 x 和点 y 之间存在一条边长度为 k;

再接下来 m 行,每行两个整数 x,y,表示询问点 x 到点 y 的最短距离。

树中结点编号从 1 到 n。

输出格式
共 m 行,对于每次询问,输出一行询问结果。

数据范围
2≤n≤104,
1≤m≤2×104,
0<k≤100,
1≤x,y≤n
输入样例1:
2 2
1 2 100
1 2
2 1
输出样例1:
100
100
输入样例2:
3 2
1 2 10
3 1 15
1 2
3 2
输出样例2:
10
25
难度:中等
时/空限制:1s / 64MB
总通过数:4461
总尝试数:9386
来源:《信息学奥赛一本通》
算法标签

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5;
LL idx, h[N * 2], e[N * 2], ne[N * 2], w[N *2];
LL n, m, depth[N * 2], f[N][16], p[N], len[N][16];

void add ( LL a, LL b , LL c ) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

void bfs () {
    memset( depth, 0x3f, sizeof depth );
    depth[0] = 0; depth[1] = 1;
    LL hh = 0, tt = 0;
    p[0] = 1;
    while ( hh <= tt ) {
        int t = p[hh ++ ];
        for ( int i = h[t]; ~i; i = ne[i] ) {
            LL s = e[i];
        //    cout << s << " s" << endl;
            if ( depth[s] > depth[t] + 1 ) {
                depth[s] = depth[t] + 1;
                f[s][0] = t, len[s][0] = w[i];
                for ( int i = 1; i <= 15; i++ ) {
                    f[s][i] = f[f[s][i - 1]][i - 1];
                    len[s][i] = len[len[s][i - 1]][i - 1];
                }
                p[++tt] = s;
            }
        }
    }

}

LL lca ( int a, int b ) {
    if ( depth[b] > depth[a] ) swap( a, b );
 //   cout << "lca" << endl;

    LL res = 0;
   // cout << depth[a] << " " << depth[b] << endl;
    for ( int i = 15; i >= 0; i -- ) {
        if ( depth[f[a][i]] >= depth[b] ) {
            res += len[a][i];
         //   cout << i << "   " << f[a][i] << " " << depth[b] << endl;
            a = f[a][i];
        }
    }

 //   cout << a << " debug " << b << endl;
    if ( a == b ) return res;
    for ( int i = 15; i >= 0; i -- ) {
        if ( f[a][i] != f[b][i] ) {
            res += ( len[a][i] + len[b][i] );
            a = f[a][i],b = f[b][i];
        }
    }
   // cout << res << endl;
    res += len[a][0] + len[b][0];
   // cout << res << endl;
   return res;
}

int main () {
    memset( h, -1, sizeof h );
    scanf( "%d%d", &n, &m );
    for ( int i = 1; i <= n - 1; i ++ ) {
        LL a, b, c;
        scanf( "%lld%lld%lld", &a, &b, &c );
        add( a, b, c );
        add( b, a, c );
    }
  //  cout << "in"  << endl;
    bfs ();
 //   cout << "out" << endl;
    for ( int i = 1; i <= m; i++ ) {
        LL a, b;
        scanf( "%lld%lld" , &a, &b );
        //cout << a << " " << b << endl;
        cout << lca ( a, b ) << endl;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值