HDU ~ 2586 ~ How far away ?(LCA,树上两点最短路径)

在这里插入图片描述

题意

T组测试数据,每组给你N,Q,然后给N-1条边(u,v,w)u-v边权为w,表示一棵树,Q次询问,每次询问a到b的最短路径。

思路

LCA,然后过程中记录下每个点到根节点的距离dis,dis[a] + dis[b] - 2*dis[LCA(a,b)]就是a到b的最短路径。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;

struct Edge
{
    int from, to, dist;
    Edge (int from, int to, int dist): from(from), to(to), dist(dist) {}
};
struct Query
{
    int id, a, b;
    Query (int id, int a, int b): id(id), a(a), b(b) {}
};
//tarjan离线求LCA
struct LCA
{
    int n, m, Q_cnt;
    int par[MAXN];                        //并查集
    vector<Edge> edges;
    vector<int> G[MAXN];
    vector<Query> querys;                 //查询建图
    vector<int> Q[MAXN];                  //查询的邻接表
    bool vis[MAXN];                       //是否被遍历
    int lca[MAXN];                        //答案
    int dis[MAXN];                        //到根节点的距离

    void init (int n)
    {
        this->n = n, m = 0, Q_cnt = 0;
        edges.clear(), querys.clear();
        for (int i = 0; i <= n; i++)
        {
            G[i].clear(), Q[i].clear();
            par[i] = i;
            dis[i] = vis[i] = 0;
        }
    }

    void AddEdge (int from, int to, int dist)
    {
        edges.emplace_back(from, to, dist);
        edges.emplace_back(to, from, dist);
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    void AddQuery (int id, int u, int v)
    {
        querys.emplace_back(id, u, v);
        querys.emplace_back(id, v, u);
        Q_cnt = querys.size();
        Q[u].push_back(Q_cnt-2);
        Q[v].push_back(Q_cnt-1);
    }

    int Find (int x) { return x == par[x] ? x : par[x] = Find(par[x]); }

    void unite (int x, int y)
    {
        int rt1 = Find(x), rt2 = Find(y);
        if (rt1 != rt2) par[rt2] = rt1;
    }

    void tarjan(int u)
    {
        vis[u] = 1;
        for (auto& i : G[u])
        {
            Edge& e = edges[i];
            int v = e.to;
            if (vis[v]) continue;
            dis[v] = dis[u] + e.dist;
            tarjan(v);
            unite(u, v);
        }
        for (auto& i : Q[u])
        {
            Query& q = querys[i];
            if (!vis[q.b]) continue;
            lca[q.id] = Find(q.b);
        }
    }

}gao;

int main()
{
    int T; scanf("%d", &T);
    while (T--)
    {
        int n, Q; scanf("%d%d", &n, &Q);
        gao.init(n);
        for (int i = 1; i < n; i++)
        {
            int u, v, w; scanf("%d%d%d", &u, &v, &w);
            gao.AddEdge(u, v, w);
        }
        for (int i = 0; i < Q; i++)
        {
            int a, b; scanf("%d%d", &a, &b);
            gao.AddQuery(i, a, b);
        }
        gao.tarjan(1);
        for (int i = 0; i < Q; i++)
        {
            Query& q = gao.querys[i*2];
            int ans = gao.dis[q.a] + gao.dis[q.b] - 2*gao.dis[gao.lca[i]];
            printf("%d\n", ans);
        }
    }
    return 0;
}
/*
2
3 2
1 2 10
3 1 15
1 2
2 3

2 2
1 2 100
1 2
2 1
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值