Tarjan LCA
以前认为Tarjan算法只是用来解强连通分量的算法,后来发现LCA也有一个Tarjan算法。
下面借一个树来了解一下算法思路。(树已经横向放置)
询问组:
11 9
10 8
6 7
2 3
这个算法最好亲手模拟一遍,然后就会感到茅塞顿开。
先从1节点开始DFS深度优先遍历(左节点优先)。
1->2->4->8->10。
发现10没有子节点,找所有关于10的询问,发现询问组有一个10和8的询问,检查发现vis[8]=0,说明8节点并没有被遍历完所有边或者根本就没有遍历过8节点,这样的话我们就不管它。
然后就把10标记上vis[10]=1,然后利用并查集,把它的(直接)父节点记录下来fa[10]=8。
然后就继续回退到8的下一个子节点,找到11,同理,检查询问组发现有11和9的询问,且vis[9]=0,跳过,vis[11]=1,fa[11]=8,回退至8。
8没有子节点可遍历了,检查询问组发现有关于8的询问10和8,而且vis[10]=1,那么我们就返回值fa[10]=8,10和8的LCA就是8,没有其余的关于8的询问了,vis[8]=1,fa[8]=4,回退至4。
4没有子节点可遍历了,检查询问组发现没有关于4的询问,那么就vis[4]=1,fa[4]=2,回退至2。
继续到5和9,9没有子节点可遍历了,检查询问组发现有关于9的询问8和9,而且vis[8]=1,那么我们就返回值findd(fa[8])=2,9和8的LCA就是2,那么就vis[9]=1,fa[9]=5,回退至5。
回退到5,没有询问,标记,回退。
2没有子节点可遍历了,检查询问组发现有关于2的询问2和3,且vis[3]=0,跳过,vis[2]=1,fa[2]=1,回退至1。
1有子节点可遍历,遍历1->3->6。
6没有子节点可遍历了,检查询问组发现有关于2的询问6和7,且vis[7]=0,跳过,vis[6]=1,fa[6]=3,回退至3。
1有子节点可遍历,遍历3->7。
7没有子节点可遍历了,检查询问组发现有关于7的询问6和7,且vis[6]=1,那么我们就返回值fa[6]=3,6和7的LCA就是3,没有其余的关于7的询问了,vis[7]=1,fa[7]=3,回退至3。
3没有子节点可遍历了,检查询问组发现有关于2的询问2和3,且vis[2]=1,那么我们就返回值fa[2]=1,2和3的LCA就是1,没有其余的关于3的询问了,vis[3]=1,fa[3]=1,回退至1。
1没有子节点可遍历了,检查询问组发现没有关于1的询问,结束算法。
鸣谢:yangbowen2同学的纠正!
代码实现比较简单。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define MAXX 500001
using namespace std;
int temp=0,i,j,m,n,s;
int tree_hd[MAXX],tree_y[MAXX*2],tree_nxt[MAXX*2];
int ask_hd[MAXX],ask_y[MAXX*2],ask_nxt[MAXX*2];
int ask_xx[MAXX],ask_yy[MAXX];
int fa[MAXX],deep[MAXX],vis[MAXX];
int ans[MAXX*4],ans_b[MAXX];
int r()
{
int aans=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
aans*=10;
aans+=ch-'0';
ch=getchar();
}
return aans*f;
}
void tree_add(int xx,int yy)
{
tree_y[++temp]=yy;
tree_nxt[temp]=tree_hd[xx];
tree_hd[xx]=temp;
}
void ask_add(int xx,int yy)
{
ask_y[++temp]=yy;
ask_nxt[temp]=ask_hd[xx];
ask_hd[xx]=temp;
}
int findd(int fdx)
{
if(fa[fdx]==fdx) return fdx;
fa[fdx]=findd(fa[fdx]);
return fa[fdx];
}
void dfs(int x)
{
int son;
for(int p=tree_hd[x];p;p=tree_nxt[p])
{
son=tree_y[p];
if(!deep[son])
{
deep[son]=deep[x]+1;
dfs(son);
fa[son]=x;
vis[son]=1;
}
}
for(int qq=ask_hd[x];qq;qq=ask_nxt[qq])
{
if(vis[ask_y[qq]])
{
ans[qq]=findd(ask_y[qq]);
}
}
}
void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int main()
{
freopen("lca.in","r",stdin);
freopen("lca.out","w",stdout);
n=r(),m=r(),s=r();
int xx,yy;
for(i=1; i<n; i++)
{
xx=r(),yy=r();
tree_add(xx,yy);
tree_add(yy,xx);
}
for(i=1;i<=n;i++)
{
fa[i]=i;
}
for(j=1; j<=m; j++)
{
ask_xx[j]=r(),ask_yy[j]=r();
ask_add(ask_xx[j],ask_yy[j]);
ask_add(ask_yy[j],ask_xx[j]);
}
deep[s]=1;
dfs(s);
for(i=1;i<=m;i++)
{
if(ans[i*2-1])
write(ans[i*2-1]),putchar(10);
else if(ans[i*2])
write(ans[i*2]),putchar(10);
else
m++;
}
}