HDU - 2586 How far away ?-- LCA - Tarjan 算法(无向图、带权边)

本文详细介绍了如何使用LCA-tarjan算法解决求两点间距离的问题,通过实例讲解了算法的实现过程,包括使用前向星存图、并查集合并节点以及dfs遍历等关键步骤。

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

题目链接http://hdu.hustoj.com/showproblem.php?pid=2586

题意:N 个节点,N-1 条边,任意两点之间只有唯一的一条路径。给出 M 个询问,求给出的询问中两点间的距离

思路:求 B、C 两点间的距离,设 A 点为 B、C 两点的最近公共祖先,D 为任意一点,则有 |BC| = |BD| + |CD| - 2*|AD|。所以此题用 LCA - tarjan 算法求出两点的最近公共祖先即可。


有关 Tarjan 算法的介绍

 https://blog.youkuaiyun.com/cutedumpling/article/details/83188775 

 https://www.cnblogs.com/JVxie/p/4854719.html 


解题步骤:

1、用数组模拟邻接表(前向星存图)可以解决 Tarjan 算法对于询问产生的结果无序的问题。

2、选取节点 1 为根节点,用 dis 数组来储存任意一点到节点 1 的距离。

3、用 dfs 遍历所有节点,用并查集来合并节点。

4、注意无向图在遍历与当前 u 有询问关系的节点时,将结果赋值给反向边。


AC代码如下:

#include<bits/stdc++.h>
using namespace std;

#define runfile freopen("E:/Code/CB/Root/data.txt", "r", stdin)
#define stopfile fclose(stdin)

const int maxn = 40005;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
int n,m,first[maxn],first1[maxn],flag[maxn],vis[maxn],f[maxn],dis[maxn];

struct edge{
    int u,v,w,next;
    edge(){}
    edge(int _u, int _v, int _w, int _next) : u(_u), v(_v), w(_w), next(_next){}
}g[2*maxn], query[2*maxn];

void init()
{
    memset(first, -1, sizeof(first));
    memset(first1, -1, sizeof(first1));
    memset(flag, 0, sizeof(flag));
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= n; i++)
    {
        f[i] = i;
        dis[i] = 0;
    }
}

void add_edge(int i, int from, int to, int cost)
{
    g[i] = edge(from, to, cost, first[from]);
    first[from] = i;
    //无向图
    g[i+n-1] = edge(to, from, cost, first[to]);
    first[to] = i+n-1;
}

void add_edge1(int i, int from, int to)
{
    query[i] = edge(from, to, 0, first1[from]);
    first1[from] = i;
    //无向图
    query[i+m] = edge(to, from, 0, first1[to]);
    first1[to] = i+m;
}

int Find(int x)
{
    while(x != f[x])    x = f[x];
    return f[x];
}

void Merge(int x, int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if(fx == fy) return;
    f[x] = y;
}

void tarjan(int u)
{
    flag[u] = 1;
    for(int i = first[u]; i != -1; i = g[i].next)
    {
        if(!flag[g[i].v])
        {
            dis[g[i].v] = dis[u] + g[i].w;
            tarjan(g[i].v);
            vis[g[i].v] = 1;
            Merge(g[i].v, u);
        }
    }
    //如果是叶子节点
    for(int i = first1[u]; i != -1; i = query[i].next)
    {
        if(vis[query[i].v])
        {
            query[i].w = dis[u] + dis[query[i].v] - 2*dis[Find(query[i].v)];
            //处理输入中出现反向边的情况,例如 1、2,2、1
            //无向图
            if(i > m-1)
                query[i-m].w = query[i].w;
            else
                query[i+m].w = query[i].w;
        }
    }
}

void print()
{
    for(int i = 0; i < m; i++)
        cout<<query[i].w<<endl;
}

int main()
{
    //runfile;
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        init();
        int from,to,cost;
        for(int i = 0; i < n-1; i++)
        {
            cin>>from>>to>>cost;
            add_edge(i,from, to, cost);
        }
        for(int i = 0; i < m; i++)
        {
            cin>>from>>to;
            add_edge1(i, from, to);
        }
        tarjan(1);
        print();
    }

    //stopfile;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值