#lCAybttg040405. 1556:Dis

1556:Dis

题目描述

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

注意:边是双向的。


输入格式

第一行为两个整数 nnmmnn 表示点数,mm 表示询问次数;

接下来 n1n-1 行,每行三个整数 x,y,kx, y, k,表示点 xx 和点 yy 之间存在一条边长度为 kk

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


输出格式

输出 mm 行。对于每次询问,输出一行结果。


样例

输入样例 1

2 2 
1 2 100 
1 2 
2 1

输出样例 1

100
100

样例解释 1

树只有一条边,连接 1122,长度为 100100,所以任意询问两点距离都是 100100


输入样例 2

3 2
1 2 10
3 1 15
1 2
3 2

输出样例 2

10
25

样例解释 2

树的结构:

    1
   / \
 10   15
 /     \
2       3
  • 询问 1122:距离 1010
  • 询问 3322:路径 3123 \to 1 \to 2,距离 15+10=2515 + 10 = 25

数据范围

  • 2n1042 \le n \le 10^4
  • 1m2×1041 \le m \le 2 \times 10^4
  • 0<k1000 < k \le 100
  • 1x,yn1 \le x, y \le n

时间限制:1000 ms
内存限制:524288 KB


提示

这是一个标准的树上两点距离查询问题。

对于树上任意两点 u,vu, v,设它们的最近公共祖先为 lca(u,v)lca(u, v),则距离公式为:

$$dist(u, v) = depth[u] + depth[v] - 2 \times depth[lca(u, v)]$$

其中 depth[x]depth[x] 表示节点 xx 到根节点的距离(即路径上所有边权之和),而不是深度(边数)。

另一种更直接的方法:预处理每个节点到根节点的距离 dis[i]dis[i],那么:

$$dist(u, v) = dis[u] + dis[v] - 2 \times dis[lca(u, v)]$$

这个公式成立是因为 dis[u]dis[u]dis[v]dis[v] 都包含从根到 lcalca 的路径,减去两倍的 dis[lca]dis[lca] 就得到 uuvv 的路径长度。

实现步骤:

  1. 任选一个节点作为根(比如节点 11),DFS 预处理:
    • 每个节点的深度(用于 LCA 跳跃);
    • 每个节点到根的距离 dis[i]dis[i]
    • 倍增数组 fa[u][k]fa[u][k] 表示 uu2k2^k 级祖先。
  2. 对于每个询问 (u,v)(u, v)
    • 用倍增法求出 lca(u,v)lca(u, v)
    • 输出 dis[u]+dis[v]2×dis[lca]dis[u] + dis[v] - 2 \times dis[lca]

时间复杂度:预处理 O(nlogn)O(n \log n),每次询问 O(logn)O(\log n),总复杂度 O(nlogn+mlogn)O(n \log n + m \log n),对于 n104,m2×104n \le 10^4, m \le 2 \times 10^4 完全足够。

也可以使用 Tarjan 离线算法 O(n+m)O(n+m),但倍增法更易于实现。