Tarjan离线求lca

这篇博客写的非常好

tarjan步骤

①.任选一个点为根节点,从根节点开始。

②.遍历该点u所有子节点v,并标记这些子节点v已被访问过。

③.若是v还有子节点,返回2,否则下一步。

④.合并v到u上。

⑤.寻找与当前点u有询问关系的点v。

⑥.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点。

来洛谷交一发~

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6+10;
struct edge{
	int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){ d[++cnt] = ( edge ){v,head[u]},head[u] = cnt; }
typedef pair<int,int>p;
vector<p>vec[maxn];
int n,m,s,vis[maxn],f[maxn],ans[maxn];
int find(int x){ return x==f[x]?x:f[x] = find( f[x] ); }
void tarjan(int u)
{
	vis[u] = 1;//正在遍历u的子树 
	for(int i=head[u];i;i=d[i].nxt )
	{
		int v = d[i].to;
		if( vis[v] )	continue;
		tarjan(v); f[v] = u;
	}
	for(auto v:vec[u] )
		if( vis[v.first]==2 )	ans[v.second] = find( v.first );		
	vis[u] = 2;//遍历完成 
}
int main()
{
	cin >> n >> m >> s;
	for(int i=1;i<n;i++)
	{
		int l,r; cin >> l >> r;	
		add(l,r); add(r,l); 
	}
	for(int i=1;i<=n;i++)	f[i] = i;
	for(int i=1;i<=m;i++)
	{
		int l,r; scanf("%d%d",&l,&r);
		vec[l].push_back( p(r,i) );
		vec[r].push_back( p(l,i) );
	}
	tarjan(s);
	for(int i=1;i<=m;i++)	printf("%d\n",ans[i] );
}
上一篇:tarjan


下一篇:我的SB错误合集