童程OJ:树上任意两点的距离 带权LCA
描述
给出 n 个点的一棵树,多次询问两点之间的最短距离。
注意:边是双向的。
输入描述
第一行为两个整数 n 和 m。n 表示点数,m 表示询问次数;
下来 n−1 行,每行三个整数 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
提示
数据范围与提示
对于全部数据,2≤n≤104,1≤m≤2×104,0<k≤100,1≤x,y≤n。
感谢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; }