题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=2586
题目是说给定一张图,知道n - 1条边让两点相连,但是不会有两条路让两点相连,那么就是一颗树。然后又给定m个询问,问a, b两点间最短距离。
那么使用LCA求最短公共父节点即可,并且增加一个容器存放到树根的值,tarjan的时候增加一个转移值以报存其父节点离树根的距离。之后增加一个容器保存询问的顺序即可离线化处理询问。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#define rep(i, s, t) for(int i = s;i <= t;i++)
#define rap(i, s, t) for(int i = s;i >= t;i--)
using namespace std;
int n, m;
const int M = 100004;
vector<int>tree[M];
vector<int>query[M];
vector<int>queid[M];
vector<int>worth[M];
bool vis[M];
int f[M];
int ancestor[M];
int dis[M];
int ans[M];
int findd(int x)
{
return x == f[x]?f[x]:f[x] = findd(f[x]);
}
void uni(int x, int y)
{
x = findd(x);
y = findd(y);
if(x != y)
f[x] = y;
}
void init()
{
rep(i, 0, n){
vis[i] = false;
tree[i].clear();
query[i].clear();
worth[i].clear();
queid[i].clear();
f[i] = i;
dis[i] = 0;
ans[i] = 0;
}
}
void input_tree()
{
rep(i, 1, n - 1){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
tree[a].push_back(b);
worth[a].push_back(c);
tree[b].push_back(a);
worth[b].push_back(c);
}
}
void input_query()
{
rep(i, 1, m)
{
int a, b;
scanf("%d%d", &a, &b);
query[a].push_back(b);
queid[a].push_back(i);
query[b].push_back(a);
queid[b].push_back(i);
}
}
void tarjan(int x, int value)
{
dis[x] = value;
vis[x] = true;
rep(i, 0, (int)tree[x].size() - 1){
int v = tree[x][i];
if(vis[v] == true)
continue;
tarjan(v, value + worth[x][i]);
uni(x, v);
ancestor[findd(x)] = x;
}
rep(i, 0, (int)query[x].size() - 1){
int v = query[x][i];
if(vis[v])
ans[queid[x][i]] = dis[x] + dis[v] - 2 * dis[ancestor[findd(v)]];
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
init();
input_tree();
input_query();
tarjan(1, 0);
rep(i, 1, m)
printf("%d\n", ans[i]);
}
return 0;
}