Akira的OI(咸鱼)圈

LCA

Word count: 836 / Reading time: 5 min
2018/09/02 Share

倍增LCA

fa[i][j]表示i点往上跳2^j次,f[i][j]=f[f[i][j-1][j-1],这表示i点往上跳j-1次再往上跳j-1次,2^(j-1)+2^(j-1)=2^j。先将低节点上跃至与高节点同一高度,再不断同时上跃直至逼近最近公共祖先。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
#define maxn 1000001
using namespace std;
inline int read()
{
int l=0,w=1;
char ch=0;
while(ch<'0'||ch>'9')
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
l=(l<<3)+(l<<1)+(ch^'0');
ch=getchar();
}
return w*l;
}
int n,m,s,to[maxn],Next[maxn],head[maxn],lg[maxn],fa[maxn][18],depth[maxn];
void dfs(int x,int f,int dep)
{
fa[x][0]=f;
depth[x]=dep;
for(int i=head[x];i!=-1;i=Next[i])
if(to[i]!=f)
dfs(to[i],x,dep+1);
}
inline int lca(int x,int y)
{
if(depth[x]<depth[y])
swap(x,y);
while(depth[x]>depth[y])
x=fa[x][lg[depth[x]-depth[y]]];
if(x==y)
return x;
for(int i=lg[depth[x]];i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
memset(fa,-1,sizeof(fa));
memset(head,-1,sizeof(head));
int u,v,t=0;
n=read(),m=read(),s=read();
for(int i=2;i<=n;i++)
lg[i]=lg[i>>1]+1;
for(int i=1;i<n;i++)
{
u=read(),v=read();
to[t]=v,Next[t]=head[u],head[u]=t++;
to[t]=u,Next[t]=head[v],head[v]=t++;
}
dfs(s,-1,1);
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
if(fa[i][j-1]!=-1)
fa[i][j]=fa[fa[i][j-1]][j-1];
while(m--)
{
u=read(),v=read();
printf("%d\n",lca(u,v));
}
return 0;
}

树剖LCA

链头更低的链依次上跃,直至上跃至同一条链,高度更高的节点即是最近公共祖先。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
#define maxn 1000001
using namespace std;
inline int read()
{
int l=0,w=1;
char ch=0;
while(ch<'0'||ch>'9')
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
l=(l<<3)+(l<<1)+(ch^'0');
ch=getchar();
}
return w*l;
}
int n,m,s,to[maxn],Next[maxn],head[maxn],fa[maxn],depth[maxn],size[maxn],tot,dfsx[maxn],top[maxn],son[maxn];
void dfs1(int x)
{
size[x]=1;
for(int i=head[x];i!=-1;i=Next[i])
if(to[i]!=fa[x])
{
fa[to[i]]=x;
depth[to[i]]=depth[x]+1;
dfs1(to[i]);
size[x]+=size[to[i]];
if(size[son[x]]<size[to[i]])
son[x]=to[i];
}
}
void dfs2(int x,int tp)
{
dfsx[x]=++tot;
top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i!=-1;i=Next[i])
if(to[i]!=fa[x]&&to[i]!=son[x])
dfs2(to[i],to[i]);
}
inline int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(depth[top[x]]<depth[top[y]])
swap(x,y);
x=fa[top[x]];
}
if(depth[x]>depth[y])
swap(x,y);
return x;
}
int main()
{
memset(head,-1,sizeof(head));
int u,v,t=0;
n=read(),m=read(),s=read();
for(int i=1;i<n;i++)
{
u=read(),v=read();
to[t]=v,Next[t]=head[u],head[u]=t++;
to[t]=u,Next[t]=head[v],head[v]=t++;
}
dfs1(s);
dfs2(s,s);
while(m--)
{
u=read(),v=read();
printf("%d\n",lca(u,v));
}
return 0;
}

tarjanLCA

CATALOG
  1. 1. 倍增LCA
  2. 2. 树剖LCA
  3. 3. tarjanLCA