童程OJ:树上任意两点的距离 带权LCA

发布时间 2023-09-28 02:05:59作者: CRt0729

描述

给出 n 个点的一棵树,多次询问两点之间的最短距离。
注意:边是双向的。

输入描述

第一行为两个整数 n 和 mn 表示点数,m 表示询问次数;
下来 n1 行,每行三个整数 x,y,k,表示点 x 和点 y 之间存在一条边长度为 k;
再接下来 m 行,每行两个整数 x,y,表示询问点 x 到点 y 的最短距离。

输出描述

输出 m 行。对于每次询问,输出一行。

用例输入 1 

2 2
1 2 100
1 2
2 1

用例输出 1 

100
100

提示

数据范围与提示

对于全部数据,2n104,1m2×104,0<k100,1x,yn。

感谢chatgpt 4.0提供的代码和注释

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MAXN = 10010; // 定义最大节点数
vector<pair<int, int>> g[MAXN]; // 定义图,每个节点都有一个与之相连的节点列表,每个连接都有一个权重
int depth[MAXN], parent[MAXN], dist[MAXN]; // 定义每个节点的深度,父节点和到根节点的距离

void dfs(int v, int p, int d, int w) { // 深度优先搜索函数,用于预处理每个节点的深度和父节点
    depth[v] = d; // 设置节点v的深度
    parent[v] = p; // 设置节点v的父节点
    dist[v] = w; // 设置节点v到根节点的距离
    for (int i = 0; i < g[v].size(); i++) { // 遍历节点v的所有相邻节点
        pair<int, int> e = g[v][i];
        if (e.first != p) dfs(e.first, v, d + 1, w + e.second); // 如果相邻节点不是父节点,则递归调用dfs函数
    }
}

int lca(int u, int v) { // 最近公共祖先函数,用于查询两个节点的最近公共祖先
    if (depth[u] > depth[v]) swap(u, v); // 如果节点u的深度大于节点v的深度,则交换u和v
    while (depth[u] != depth[v]) { // 当节点u和节点v的深度不相等时,将节点v向上移动到与节点u相同的深度
        v = parent[v];
    }
    while (u != v) { // 当节点u和节点v不相同时,同时将节点u和节点v向上移动,直到它们相遇
        u = parent[u];
        v = parent[v];
    }
    return u; // 返回最近公共祖先
}

int main() { // 主函数
    int n, m;
    cin >> n >> m; // 读取节点数和查询数
    for (int i = 0; i < n - 1; i++) { // 读取每条边的信息
        int x, y, k;
        cin >> x >> y >> k;
        x--; y--; // 将节点编号减1,使其从0开始
        g[x].push_back({y, k}); // 将节点y添加到节点x的相邻节点列表中
        g[y].push_back({x, k}); // 将节点x添加到节点y的相邻节点列表中
    }
    dfs(0, -1, 0, 0); // 从根节点开始进行深度优先搜索
    for (int i = 0; i < m; i++) { // 对每个查询进行处理
        int x, y;
        cin >> x >> y; // 读取查询的两个节点
        x--; y--; // 将节点编号减1,使其从0开始
        int p = lca(x, y); // 查询两个节点的最近公共祖先
        cout << dist[x] + dist[y] - 2 * dist[p] << endl; // 计算两个节点之间的最短距离并输出
    }
    return 0;
}