|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
const int N = 10010, M = N << 1;
|
|
|
typedef pair<int, int> PII;
|
|
|
|
|
|
// 查询数组,first:对端节点号,second:问题序号
|
|
|
// 比如:q[2]={5,10} 表示10号问题,计算2和5之间的最短距离
|
|
|
vector<PII> query[N];
|
|
|
|
|
|
int dist[N]; // dist[u]记录从出发点S到u的距离
|
|
|
int res[M]; // 结果数组,有多少个问题就有多少个res[i]
|
|
|
|
|
|
// 链式前向星
|
|
|
int e[M], h[N], idx, w[M], ne[M];
|
|
|
void add(int a, int b, int c = 0) {
|
|
|
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
|
|
|
}
|
|
|
|
|
|
// 并查集
|
|
|
int p[N];
|
|
|
int find(int x) {
|
|
|
if (x != p[x]) p[x] = find(p[x]);
|
|
|
return p[x];
|
|
|
}
|
|
|
|
|
|
int st[N]; // 0:未入栈, 1:在栈中, 2:已出栈
|
|
|
void tarjan(int u) {
|
|
|
// ① 标识u已访问
|
|
|
st[u] = 1;
|
|
|
// ② 枚举与u临边相连并且没有访问过的点
|
|
|
for (int i = h[u]; ~i; i = ne[i]) {
|
|
|
int v = e[i];
|
|
|
if (!st[v]) {
|
|
|
// 扩展:更新距离
|
|
|
dist[v] = dist[u] + w[i];
|
|
|
// 深搜
|
|
|
tarjan(v);
|
|
|
// ③ v加入u家族
|
|
|
p[v] = u;
|
|
|
}
|
|
|
}
|
|
|
// ④ 枚举已完成访问的点,记录lca或题目要求的结果
|
|
|
for (auto q : query[u]) {
|
|
|
int v = q.first, id = q.second;
|
|
|
if (st[v] == 2) res[id] = dist[u] + dist[v] - 2 * dist[find(v)];
|
|
|
}
|
|
|
// 表示该点已经回溯
|
|
|
st[u] = 2;
|
|
|
}
|
|
|
int main() {
|
|
|
int n, m; // n个结点,m次询问
|
|
|
scanf("%d%d", &n, &m);
|
|
|
|
|
|
memset(h, -1, sizeof h); // 初始化链式前向星
|
|
|
for (int i = 1; i <= n; i++) p[i] = i; // 并查集初始化
|
|
|
|
|
|
for (int i = 1; i < n; i++) { // 树有n-1条边
|
|
|
int a, b, c;
|
|
|
scanf("%d%d%d", &a, &b, &c);
|
|
|
add(a, b, c), add(b, a, c); // 无向图
|
|
|
}
|
|
|
|
|
|
// Tarjan算法是离线算法,一次性读入所有的问题,最终一并回答
|
|
|
for (int i = 0; i < m; i++) { // m个询问
|
|
|
int a, b;
|
|
|
scanf("%d%d", &a, &b); // 表示询问点 a 到点 b 的最短距离
|
|
|
query[a].push_back({b, i}), query[b].push_back({a, i}); // 不知道谁先被遍历 所以正反都记一下着
|
|
|
}
|
|
|
|
|
|
// tarjan算法求LCA
|
|
|
tarjan(1);
|
|
|
|
|
|
// 回答m个问题
|
|
|
for (int i = 0; i < m; i++) printf("%d\n", res[i]);
|
|
|
|
|
|
return 0;
|
|
|
} |