Description
给出 n 个点的一棵树,多次询问两点之间的最短距离。
注意: 边是无向的。
所有节点的编号是 1, 2, …, n 。
Input
第一行为两个整数 n , m ( 2 ≤ n ≤ 104 , 1 ≤ m ≤ 2×104 )。n 表示点数,m 表示询问次数;
下来 n - 1 行,每行三个整数 x, y, k ( 1 ≤ x, y ≤ n , 0 < k ≤ 100 ),表示点 x 和点 y 之间存在一条边长度为 k;
再接下来 m 行,每行两个整数 x, y,表示询问点 x 到点 y 的最短距离。
树中节点编号从 1 到 n 。
Output
共 m 行,对于每次询问,输出一行询问结果
Sample Input
sample #1
2 2
1 2 100
1 2
2 1
sample #2
3 2
1 2 10
3 1 15
1 2
3 2Sample Output
sample #1
100
100
sample #2
10
25
tarjan求LCA:
属于离线做法:需要先记录每个询问,在做完tarjan算法后将每个询问输出
在深搜时,利用st[]代表各结点的状态,
st[i] = 1表示该节点在当前搜索的分支中;
st[i] = 2表示该节点的子树全部被搜索过;
st[i] = 0表示该节点未被搜索过。
并查集的使用:在每次回溯时添加回溯节点为父节点,在回溯到当前节点时,寻找包含当前节点的询问,若询问中的另一节点状态st[i]=2,则LCA为另一节点的祖宗节点;否则跳过该询问。
下面通过举例,模拟tarjan算法过程
假设题目要求查询8和5,4和5,2和4的LCA
利用深搜,首先从根节点1向下遍历,st[1] = 1;
继而遍历到节点2,st[2] = 1;
继而遍历到节点4,st[4] = 1;
继而遍历到8,st[8] = 1;此时发现8没有子树,于是令st[8]=2;访问另一节点5,因为st[5]!=2,所以跳过该询问。
继而回溯到4,令per[8] = 4;st[4] = 2;此时访问另一节点5,因为st[5]!=2,所以跳过该询问。访问另一节点2,因为st[2]!=2,因此跳过该询问。
继而回溯到2,令per[4] = 2;st[2] = 2;此时访问另一节点4,因为st[4]=2,所以2和4的LCA为find(4)=2;
继而访问5,令st[5] = 1;此时发现5没有子树,于是令st[5]=2;访问另一节点8,因为st[8] = 2;因此5和8的LCA为find(8) = 2;访问另一节点4,因为st[4] = 2;因此5和4的LCA为find(4) = 2;
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
#define N 20010
typedef pair<int, int> pii;
int n, m;
vector<pii>qurey[N];//其中sec存放查询编号,fir存放另一个查询节点
int p[N];
int dist[N], st[N], ans[N];
int h[N], e[N*2], w[N*2], ne[N*2], idx;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int find(int x) {
if (x != p[x])p[x] = find(p[x]);
return p[x];
}
void dfs(int x, int fa) {
for (int i = h[x]; i != -1; i = ne[i]) {
int j = e[i];
if (j == fa)continue;
dist[j] = dist[x] + w[i];
dfs(j, x);
}
}
void tarjan(int u) {
st[u] = 1;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j]) {
tarjan(j);
p[j] = u;
}
}
for (pii t : qurey[u]) {
int ver = t.first, id = t.second;
if (st[ver] == 2) {
int lca = find(ver);
ans[id] = dist[ver] + dist[u] - 2 * dist[lca];
}
}st[u] = 2;
}
int a, b, c;
int main() {
memset(h,-1,sizeof h);
cin >> n >> m;
for (int i = 0; i < n - 1; i++) {
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
for (int i = 0; i < m; i++) {
cin >> a >> b;
if (a != b) {//存储询问节点,当a==b时不处理,ans[i]全局变量为0
qurey[a].push_back({ b,i });
qurey[b].push_back({ a,i });
}
}
for (int i = 1; i <= n; i++)p[i] = i;
dfs(1, -1);
tarjan(1);
for (int i = 0; i < m; i++)cout << ans[i] << endl;
return 0;
}