luogu3379为例
1、倍增算法(第三快
#include <cstdio>
#include <algorithm>
#define maxn 500005
using namespace std;
struct node
{
int to,next;
}E[maxn*2];
int n,N,m,root,tot,lg[maxn],head[maxn],to[maxn],next[maxn],dep[maxn],fa[20][maxn];
void add_edge(int u,int v)
{
E[++tot].to=v;
E[tot].next=head[u];
head[u]=tot;
}
void dfs(int u,int p)
{
dep[u]=dep[p]+1;
fa[0][u]=p;
for(int i=1;(1<<i)<=dep[u];i++)
fa[i][u]=fa[i-1][fa[i-1][u]];
for(int v=head[u];v;v=E[v].next)
if(p!=E[v].to)dfs(E[v].to,u);
}
int get_lca(int u,int v)
{
if(dep[u]<dep[v])swap(u,v);
while(dep[u]>dep[v])
u=fa[lg[dep[u]-dep[v]]-1][u];
if(u==v)return u;
for(int k=lg[dep[u]]-1;k>=0;k--)
if(fa[k][u]!=fa[k][v])
u=fa[k][u],v=fa[k][v];
return fa[0][u];
}
int main()
{
scanf("%d%d%d",&n,&m,&root);
int u,v;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs(root,0);
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
printf("%d\n",get_lca(u,v));
}
return 0;
}
2、tarjon算法(第二快
#include <cstdio>
#define maxn 1000005
using namespace std;
struct node
{
int to,next;
}E[maxn*2];
struct noded
{
int to,next,oppo,num;
bool flag;
}G[maxn*2];
bool vis[maxn];
int head[maxn],m,n,root,tot,fa[maxn],list[maxn],ans[maxn];
void add_edge(int u,int v)
{
E[++tot].to=v;
E[tot].next=head[u];
head[u]=tot;
}
void plus_edge(int u,int v)
{
G[++tot].to=v;
G[tot].next=list[u];
list[u]=tot;
}
int find(int p)
{
if(fa[p]==p)return p;
return fa[p]=find(fa[p]);
}
void unionn(int a,int b)
{
fa[find(a)]=find(b);
}
void tarjon(int u,int p)
{
for(int i=head[u];i;i=E[i].next)
{
int v=E[i].to;
if(v!=p&&!vis[v])
{
tarjon(v,u);
unionn(v,u);
vis[v]=1;
}
}
for(int i=list[u];i;i=G[i].next)
{
int v=G[i].to;
if(vis[v]&&!G[i].flag)
{
ans[G[i].num]=find(v);
int j=G[i].oppo;
G[i].flag=G[j].flag=true;
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&root);
int u,v;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
plus_edge(u,v);
G[tot].oppo=tot+1;
G[tot].num=i;
plus_edge(v,u);
G[tot].oppo=tot-1;
G[tot].num=i;
}
tarjon(root,0);
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
3、树链剖分(最快
过程:
首先预处理每个子树的大小和每个节点的深度,并对每个点选择子树大小最大的二字作为重儿子,然后顺着重儿子dfs,把重链顶点标记到每个点上。
接着求LCA时,就从两个点选dep[top[x]]更大的,跳到fa[top[x]],直到两个节点都在同一条重链上(即top[x]相等)
这样我们就在O(n)预处理的情况下,O(log n)地在线回答了LCA问题,并且常数因子非常小,比倍增的要小得多
代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#define maxn 500005
using namespace std;
struct node
{
int to,next;
}E[maxn*2];
int tot,m,n,root,head[maxn],dep[maxn],son[maxn],top[maxn],size[maxn],fa[maxn];
void add_edge(int u,int v)
{
E[++tot].to=v;
E[tot].next=head[u];
head[u]=tot;
}
void dfs1(int u,int p)
{
dep[u]=dep[p]+1;
size[u]=1;
for(int i=head[u];i;i=E[i].next)
{
int v=E[i].to;
if(v==p)continue;
fa[v]=u;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]])son[u]=v;
}
}
void dfs2(int u,int topp)
{
top[u]=topp;
if(son[u])dfs2(son[u],topp);
for(int i=head[u];i;i=E[i].next)
{
int v=E[i].to;
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
int get_lca(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]])swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
int main()
{
scanf("%d%d%d",&n,&m,&root);
int u,v;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs1(root,0);
dfs2(root,root);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
printf("%d\n",get_lca(u,v));
}
return 0;
}