前言:
md,感觉LCA上我的学习顺序正好反过来了。先学的ST+欧拉序,后学的倍增。最后才学的Tarjan求LCA。
这个算法唯一不足就是离线的,但是一大好处就是实现起来超级简单。理解起来也很简单.
本质上就是一个dfs。
我们对每一个点开一个vector存询问。对于任意两个点 u , v u,v u,v。dfs的时候要么先遍历u再遍历v.要么反之。所以我们需要对 u , v u,v u,v都插入询问。
当访问到 v v v时,若 u u u已经被访问了,那么 L C A ( u , v ) LCA(u,v) LCA(u,v)这个点一定是被访问且未结束访问的点。通过 u u u找 L C A ( u , v ) LCA(u,v) LCA(u,v)可以用并查集来优化。
讲的太乱了,还是看这篇博客吧:
https://segmentfault.com/a/1190000015145319?utm_source=index-hottest
具体看代码:
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int maxn = 5e5 + 5;
const int maxm = 1e6 + 5;
int u[maxm] , v[maxm] , nextt[maxm] ,first[maxn] , sign;
void addedge(int x , int y){u[++sign]=x;v[sign]=y;nextt[sign]=first[x];first[x]=sign;}
vector<pii> q[maxn];
int res[maxm];
int f[maxn];
int getf (int x) {return x == f[x] ? x : f[x] = getf(f[x]);}
bool mer (int a , int b)
{
int fa = getf(a) , fb = getf(b);
if (fa == fb) return false;
f[fb] = fa;
return true;
}
int vis[maxn];
void dfs (int g , int fa)
{
vis[g] = 1;
for (int i = first[g] ; i ; i = nextt[i]){
if (v[i] == fa) continue;
dfs(v[i] , g);
mer(g , v[i]);
}
for (auto & k : q[g]){
if (vis[k.first] == 0) continue;
res[k.second] = getf(k.first);
// cout << "现在在" << g << " " << k.first << " " << getf(k.first) << endl;
}
}
int main()
{
int n , m , s;
scanf("%d%d%d" , &n , &m , &s);
for (int i = 1 ; i <= n ; i++){
f[i] = i;
}
for (int i = 1 ; i < n ; i++){
int x , y;
scanf("%d%d" , &x , &y);
addedge(x , y);
addedge(y , x);
}
for (int i = 1 ; i <= m ; i++){
int x , y;
scanf("%d%d" , &x , &y);
q[x].push_back(make_pair(y , i));
q[y].push_back(make_pair(x , i));
}
dfs(s , 0);
for (int i = 1 ; i <= m ; i++){
printf("%d\n" , res[i]);
}
return 0;
}