版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/83351201
一.概述.
tarjan求LCA是一种可以在的时间复杂度内处理离线LCA的方式.但是一旦遇到了在线询问,tarjan求LCA就失效了.
二.算法流程.
tarjan算法本质上是对朴素LCA的一个优化,它是基于dfs进行优化的.
我们考虑开一个n个vector,其中q[i]表示与i相关的询问点.
然后我们考虑一个dfs,当我们遍历到点x的时候,枚举与x相关的所有询问,当一个点y已经被遍历过的时候,显然y与x的LCA就是y的祖先中第一个被搜过但没有退出搜索的点.
为了维护这个过程,我们引入3种标记,其中0表示点未被搜过,1表示点被搜到但没有退出搜索,2表示点已经退出搜索.
之后当遍历一个点x时,枚举与x相关询问时,就可以让y从当前点开始往上爬,爬到的第一个标记为1的点即为这个询问的LCA.这个算法的时间复杂度最坏为.
为了优化这个算法,我们可以考虑使用并查集,当一个点被标记为1时,就创建一个以它为根的并查集;当一个点被标记为2时,就把它所处的并查集并到它的父亲上,这样就可以做到.
至于为什么并查集的log没有算上,是因为这个并查集往上并的时候直接可以将根定为它的父亲,这个操作的复杂度为,而get操作最多只会将整个并查集遍历一遍,时间复杂度之和最多为.
三.模板题及代码.
题目:luogu3379.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
#define pb push_back
typedef long long LL;
int ri(){
int x=0;
char c;
for (c=getchar();c<'0'||c>'9';c=getchar());
for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
return x;
}
const int N=500000;
struct side{
int y,next;
}e[N*2+9];
int n,m,lin[N+9],top,root;
vector<int>q[N+9],id[N+9];
int ans[N+9];
int fa[N+9],vis[N+9];
int get(int u){return fa[u]^u?fa[u]=get(fa[u]):u;}
void ins(int x,int y){
e[++top].y=y;
e[top].next=lin[x];
lin[x]=top;
}
void dfs(int k){
fa[k]=k;
vis[k]=1;
for (int i=lin[k];i;i=e[i].next)
if (!vis[e[i].y]){
dfs(e[i].y);
fa[e[i].y]=k;
}
vis[k]=2;
int len=q[k].size();
for (int i=0;i<len;++i)
if (vis[q[k][i]]==2) ans[id[k][i]]=get(q[k][i]);
}
Abigail into(){
n=ri();m=ri();root=ri();
int x,y;
for (int i=1;i<n;++i){
x=ri();y=ri();
ins(x,y);ins(y,x);
}
for (int i=1;i<=m;++i){
x=ri();y=ri();
q[x].pb(y);id[x].pb(i);
q[y].pb(x);id[y].pb(i);
}
}
Abigail work(){
dfs(root);
}
Abigail outo(){
for (int i=1;i<=m;++i)
printf("%d\n",ans[i]);
}
int main(){
into();
work();
outo();
return 0;
}