题意
一个村子中的房子被一些长度不等的道路链接,保证无环(即构成一颗树),给出一些询问,询问两个房子之间的距离是多少。
思路
很明显的求最近公共祖先,使用Tarjan算法,在求LCA的过程中,记录节点到根的距离dis,然后对于每个询问,则 ans=dis[u]+dis[v]−2∗dis[lca] 。
代码
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 4e4+5;
const int maxq = 205;
struct Edge{
int to,cost;
Edge(int t,int c):to(t),cost(c){};
};
struct Que{
int to,lca,idx;
Que(int t,int i):to(t),idx(i){};
};
vector <Edge> G[maxn];
vector <Que> Ques[maxn];
bool vis[maxn];
int dis[maxn];
int ans[maxq];
int fa[maxn];
void init(int n){
for(int i = 0 ; i <= n ; i ++){
G[i].clear();
Ques[i].clear();
fa[i] = i;
}
memset(dis,0,sizeof dis);
memset(ans,0,sizeof ans);
memset(vis,0,sizeof vis);
}
void addedge(int from, int to, int cost){
G[from].push_back(Edge(to,cost));
G[to].push_back(Edge(from,cost));
}
void addQue(int from, int to, int idx){
Ques[from].push_back(Que(to,idx));
Ques[to].push_back(Que(from,idx));
}
int Find(int x){
return fa[x] == x ? fa[x]:fa[x] = Find(fa[x]);
}
void Union(int x, int y){
int a = Find(x);
int b = Find(y);
if(a!=b) fa[b] = a;
}
void Tarjan(int cur){
vis[cur] = true;
for(int i = 0 ; i < G[cur].size(); i ++){
Edge& e = G[cur][i];
if(!vis[e.to]){
dis[e.to] = dis[cur] + e.cost;
Tarjan(e.to);
Union(cur,e.to);
}
}
for(int i = 0 ; i < Ques[cur].size() ; i ++){
Que & q = Ques[cur][i];
if(vis[q.to]){
q.lca = Find(q.to);
ans[q.idx] = dis[cur] + dis[q.to] - 2*dis[q.lca];
}
}
}
int main(){
int T;
scanf("%d", &T);
while(T --){
int n,q;
scanf("%d %d",&n,&q);
init(n);
for(int i = 1 ; i < n ; i ++){
int u,v,cost;
scanf("%d %d %d",&u,&v,&cost);
addedge(u,v,cost);
}
for(int i = 0 ; i < q ; i ++){
int u,v;
scanf("%d %d",&u,&v);
addQue(u,v,i);
}
Tarjan(1);
for(int i = 0 ; i < q; i ++){
printf("%d\n",ans[i]);
}
}
return 0;
}