LCA - tarjan实现

咸鱼了很多天,开始补题,进入了递归学习模式:
生成树专题->最小生成树专题->MST + LCA-> LCA -> LCA(tarjan实现)

LCA的tarjan实现

和tarjan找强连通分量类似,在DFS增加一些骚操作使得在搜索的过程中完成公共祖先的预处理。所以这是一种离线算法,复杂度O(n + q)。

思路

假设a点和b点的最近公共祖先是x,那么在DFS时访问的这三个节点的顺序必然是
到达节点x,继续搜索子树,到达a,返回附近节点,返回x,继续搜索子树,到达b。(a,b访问顺序可互换)
所以我们用并查集,在访问完一颗子树now后,把now归并到其父亲节点的集合中去。这样在访问到b时,x到a这段路径上的fa数组均已更新,所以find(a) = x,a和b的最近公共祖先就可以表示为find(a)。

伪代码

void tarjan(int now){
    
    
	vis[now] = 1;
	fa[now] = now;
	for(each {
    
    x,y}){
    
    //枚举所有要查询最近公共祖先的点对
		//如果当前点和有另一个已经访问过的点要求lca
		if(x == now && vis[y])z[i] = find(y);
		if(y == now && vis[x])z[i] = find(x);
		//用z数组代表答案
	}
	for(int i = 0 ; i < G[now].size() ; i++){
    
    
	//继续tarjan
		edge e = G[now][i];
		if(!vis[e.to]){
    
    
			dis[e.to] = dis[now] + e.val;
			tarjan(e.to);
			fa[e.to] = now;
		}
	}
}

例题

hdu2586
AC代码
这里用邻接表存的查询数组,感觉复杂度会到O(n*q)
求树上最短路可以转换成dis[x] + dis[y] - 2 * dis[lca(x,y)],妙啊

#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn = 4e4 + 5;
const int maxm = 205;
struct edge{
    
    
	int to, val;
};

vector<edge>G[maxn];
int x[maxm],y[maxm],z[maxm];
	
int dis[maxn],fa[maxn],vis[maxn];
int n, m;

int find(int now){
    
    
	if(fa[now] == now)return now;
	return fa[now] = find(fa[now]);
}

void tarjan(int now){
    
    
	vis[now] = 1;
	fa[now] = now;
	for(int i = 1 ; i <= m ; i++){
    
    
		if(x[i] == now && vis[y[i]]) z[i] = find(y[i]);
		if(y[i] == now && vis[x[i]]) z[i] = find(x[i]);
	}
	for(int i = 0 ; i < G[now].size() ; i++){
    
    
		edge e = G[now][i];
		if(!vis[e.to]){
    
    
			dis[e.to] = dis[now] + e.val;
			tarjan(e.to);
			fa[e.to] = now;
		}
	}
}

int main(){
    
    
	int T;
	scanf("%d",&T);
	while(T--){
    
    
		scanf("%d %d",&n,&m);

		for(int i = 1 ; i <= n ; i++){
    
    
			vis[i] = 0;
			G[i].clear();
		}

		int u, v, c;
		for(int i = 1 ; i < n ; i++){
    
    
			scanf("%d%d%d",&u,&v,&c);
			G[u].push_back({
    
    v,c});
			G[v].push_back({
    
    u,c});
		}
		for(int i = 1 ; i <= m ; i++){
    
    
			scanf("%d%d",&x[i],&y[i]);
		}
		
		dis[1] = 0;
		tarjan(1);
		
		for(int i = 1 ; i <= m ; i++){
    
    
			printf("%d\n",dis[x[i]] + dis[y[i]] - 2 * dis[z[i]]);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35068676/article/details/107958462