HDU - 2586 How far away ? (LCA求树上两点间距离模板题)

版权声明:本文为博主原创文章,转载请附上注明就行_(:з」∠)_。 https://blog.csdn.net/vocaloid01/article/details/82081418

There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.

Input

First line is a single integer T(T<=10), indicating the number of test cases.
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.

Output

For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.

Sample Input

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

2 2
1 2 100
1 2
2 1

Sample Output

10
25
100
100

题解:

首先要知道:树上任意两点A,B之间的距离 = 根节点到A的距离 + 根节点到B的距离 - 2*根节点到AB最近公共祖先节点的距离。

然后只要求LCA就行了。

Tarjan离线版代码:

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 40005; 
 
pair<int,int> P[205];//存询问边
int lca[205];//存询问边对应的最近公共祖先 

struct Edge{//存树边 
	int to,next,w;//w为边权 
}E[MAXN*2];

int head[MAXN],tot;

inline void Add(int from,int to,int w){
	E[++tot].next = head[from];
	E[tot].to = to;
	E[tot].w = w;
	head[from] = tot;
	E[++tot].next = head[to];
	E[tot].to = from;
	E[tot].w = w;
	head[to] = tot;
}

struct Query{//存每个点对应的询问边
	int to,next,id;
}Q[405];

int headQ[MAXN],totq;

inline void AddQ(int from,int to,int id){ 
	Q[++totq].next = headQ[from];
	Q[totq].to = to;
	Q[totq].id = id;
	headQ[from] = totq;
	Q[++totq].next = headQ[to];
	Q[totq].to = from;
	Q[totq].id = id;
	headQ[to] = totq;
} 

int dis[MAXN];//记录根节点到每个点的距离 
bool vis[MAXN];
int fa[MAXN];//并查集祖先数组 

int Find(int x){
	if(fa[x] == x)return x;
	return fa[x] = Find(fa[x]);
}

void Union(int a,int b){
	int A = Find(a);
	int B = Find(b);
	if(A != B)fa[B] = A;
}

void Tarjan(int rt){
	vis[rt] = true;
	int to;
	for(int i=head[rt] ; i ; i=E[i].next){
		to = E[i].to;
		if(!vis[to]){
			dis[to] = dis[rt] + E[i].w;
			Tarjan(to);
			Union(rt,to);
		}
	}
	for(int i=headQ[rt] ; i ; i=Q[i].next){
		to = Q[i].to;
		if(vis[to]){
			lca[Q[i].id] = Find(to);
		}
	}
}

inline void init(int n){
	for(int i=0 ; i<=n ; ++i)fa[i] = i;
	memset(vis,false,sizeof vis);
	memset(head,0,sizeof head);
	memset(headQ,0,sizeof headQ);
	tot = totq = 0;
}

int main(){
		
	int T,n,m;
	scanf("%d",&T);
	while(T--){
		scanf("%d %d",&n,&m);
		init(n);
		int a,b,c;
		for(int i=1 ; i<n ; ++i){
			scanf("%d %d %d",&a,&b,&c);
			Add(a,b,c);
		}
		for(int i=1 ; i<=m ; ++i){
			scanf("%d %d",&P[i].first,&P[i].second);
			AddQ(P[i].first,P[i].second,i);
		}
		dis[1] = 0;//这里选的根节点为 1 
		Tarjan(1);
		for(int i=1 ; i<=m ; ++i){
			printf("%d\n",dis[P[i].first]+dis[P[i].second]-2*dis[lca[i]]); 
		}
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/vocaloid01/article/details/82081418