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] );
}