线性求LCA算法

tarjan算法求LCA

离线算法,复杂度O(n + m)

简介:

\(Tarjan\) 发明的离线查询两点间最近公共祖先的算法,复杂度优秀,但由于其离线的性质导致适用面不广。

算法概述:

将整棵树的所有节点分为三种类型:

  1. 已经遍历过,并回溯的节点
  2. 正在搜索的分支
  3. 还未搜索到的节点

在搜索过程中数组 \(pd\) 记录每个节点的类型。

在图中可以发现,对于当前搜索到的节点 \(i\)\(pd_i\) = 1) 与 任意 \(j\) (\(pd_j\) = 2) 的最近公共祖先都在正在搜索的这条分支上。

\(G\) 表示 \(i\)\(j\)\(LCA\) ,观察 \(G\) 的性质 , 不难发现 ,\(G\)\(j\) 其实并无关系,只与 \(i\) 有关 , 即 \(i\) 的祖先并且 \(pd\) = 1 。

所以,我们可以利用并查集 , 在回溯的时候将节点合并到其父节点的集合中,查询的时候直接调用并查集查找即可。

目前该算法的大体框架已经理清楚了,现在我们只需要将其串联起来。

  • 将询问离线,储存在动态数组里
  • 从根节点开始 DFS ,在每次回溯的时候合并到父节点的集合中
  • 扫一遍关于当前节点 \(u\) 的所有询问 \(v\) ,如果存在 \(pd_v\) = 2 , 直接调用并查集得出答案。

以上便是 \(Tarjan\)​ 算法求解 LCA 具体思路 , 具体代码如下

code

                                        
int n , m , cnt ;                                 
int head[maxn] , pd[maxn] , dis[maxn] , fa[maxn] , ans[maxn] ; 
vector<pii > q[maxn] ; 
struct node 
{
	int to , nxt , dis ; 
} ed[maxn << 1] ; 
void add(int u , int v , int w )
{
	ed[++cnt] = { v , head[u] , w } ; 
	head[u] = cnt ; 
}
void dfs(int u , int fa)
{
	for(int i = head[u] ; i ; i = ed[i].nxt )
	{
		int v = ed[i].to , w = ed[i].dis ; 
		if(v == fa)	continue ;
		dis[v] = dis[u] + w ; 
		dfs(v , u) ; 
	}
}
int find(int x)
{
	if(fa[x] == x)	return x ; 
	return fa[x] = find(fa[x]) ; 
}
void tarjan(int u )
{
	pd[u] = 1 ; 
	for(int i = head[u] ; i ; i = ed[i].nxt )
	{
		int v = ed[i].to ; 
		if(! pd[v] )
		{
			tarjan(v) ;
			fa[v] = u ; 
		}
	}
	for(auto item : q[u] )
	{
		int v = item.first , id = item.second ; 
		if(pd[v] == 2)
		{
			int yy = find(v) ; 
			ans[id] = dis[u] + dis[v] - 2 * dis[yy] ; 
		}
	}
	pd[u] = 2 ; 
}
signed main()
{
	n = read() , m = read() ; 
	for(int i = 1 ; i < n ; i++ )
	{
		int u = read() , v = read() , w = read() ;
		add(u , v , w ) , add(v , u , w ) ;
	}
	for(int i = 1 ; i <= m ; i++ )
	{
		int u = read() , v = read() ; 
		if(u == v)	continue ;
		q[u].push_back({v , i}) ;
		q[v].push_back({u , i})	; 
	} 
	dfs(1 , 0 ) ;
	for(int i = 1 ; i <= n ; i++ )	fa[i] = i ; 
	tarjan(1 ) ; 
	for(int i = 1 ; i <= m ; i++ )
		cout << ans[i] << '\n' ; 
	return 0 ; 
}                            	

:此代码对应 例题[AcWing1171 距离](1171. 距离 - AcWing题库)

posted @ 2022-05-22 22:24  Simon_...sun  阅读(58)  评论(0)    收藏  举报