Distance Queries POJ - 1986 (树上两点的最近公共祖先)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/PK__PK/article/details/79956025

题目连接:点击打开链接

题意:因为无论如何方向如何,都能建出一棵树。所以方向这个条件可以忽略,问n个点m条边。点和点之间的权值为距离。k次查询,查任意两点的最短距离。

题解:这个一个非常经典的图论问题------LCA。

会用的一种算法:Tarjan---LCA算法。


先明白一个前题:对于有根树T而言的两个节点u,v最近公共祖先LCA(T,u,v)表示一个节点x。满足x是u,v的祖先,且x的深度尽可能大(距离u,v尽可能的近)。对于x点来说,有一点非常特殊,那么就是u到v的路径一定经过x。

这一点是这个算法的核心。明白这个前提了之后。我们再介绍一下算法

这个算法基于深度优先搜索的框架,对于一个新搜到的节点,首先创建由这个节点构成的集合,再对当前节点的每个子树进行搜索,每搜完一颗子树,则可以去欸的那个子树内所有LCA询问都已解决,其他的LCA询问的结果必然在这个子树之外,这时把子树所形成的集合和和当前节点的集合合并,并将当前节点设为集合的祖先。之后继续搜索下一棵子树,直至所有节点的所有子树搜索完。

实现过程:

建立以u为代表元素的集合。

遍历与u相连的节点v,如果没有被访问过,对于v使用Tarjan--LCA算法。结束后,将v的集合合并如u的集合

对于与u有关的询问(u,v)如果v被访问过,则结果就是v所在集合的代表元素。


那么对于这道题。我们不仅要求出来两点的公共子祖先,还有求出距离。那么我们需要开一个dist数组去记录,当前节点到根节点的距离

那么dist【v】 = dist【u】 + (u,v)的权值。

两点的距离为 dist【u】 + dist【v】 - 2*dist【find(u)】;


看代码实现:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;

using namespace std;

const int MAXN = 80080;
const int MAXNQ = 20020;

int father[MAXN];
int Head[MAXN];
int QHead[MAXN];
int Dist[MAXN];

struct EdgeNode{
	int to;
	int next ;
	int lca;
}Edges[MAXN];

EdgeNode QEdges[MAXN];

int find(int x){
	if(x != father[x])
		father[x] = find(father[x]);
	return father[x];
}

bool vis[MAXN];

int LCA(int u){
	father[u] = u;
	vis[u] = true;
	for(int k = Head[u] ; k != -1 ; k = Edges[k].next){
		if(!vis[Edges[k].to]){
			Dist[Edges[k].to] = Dist[u] + Edges[k].lca;
			LCA(Edges[k].to);
			father[Edges[k].to] = u;	
		}
	}
	for(int k = QHead[u] ; k != -1 ;k = QEdges[k].next){
		if(vis[QEdges[k].to]){
			QEdges[k].lca = Dist[u] + Dist[QEdges[k].to] - 2 * Dist[find(QEdges[k].to)];
			QEdges[k^1].lca = QEdges[k].lca;  
		}
	}
}

int main(){
	int N,M,K,u,v,w,a,b;
	char s;
	while(~scanf("%d%d",&N,&M)){
		memset(father,0,sizeof(father));
		memset(Head,-1,sizeof(Head));
		memset(QHead,-1,sizeof(QHead));
		memset(vis,0,sizeof(vis));
		memset(Edges,0,sizeof(Edges));
		memset(QEdges,0,sizeof(QEdges));
		memset(Dist,0,sizeof(Dist));
		int id = 0;
		for(int i = 0 ; i < M ; i ++){
			scanf("%d%d%d %c",&u,&v,&w,&s);
			Edges[id].to = v;
			Edges[id].lca = w;
			Edges[id].next = Head[u];
			Head[u] = id ++;
			Edges[id].to = u;
			Edges[id].lca = w;
			Edges[id].next = Head[v];
			Head[v] = id ++;
		}
		scanf("%d",&K);
		int iq = 0;
		for(int i = 0 ; i < K ; i ++){
			scanf("%d%d",&a,&b);
			QEdges[iq].to = b;
			QEdges[iq].next = QHead[a];
			QHead[a] = iq ++;
			QEdges[iq].to = a;
			QEdges[iq].next = QHead[b];
			QHead[b] = iq ++;
		}
		LCA(1);
		for(int i = 0 ; i < iq ; i += 2)
			printf("%d\n",QEdges[i].lca);
	}
	return 0;
}

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页