两个点的最近公共祖先,即两个点的所有公共祖先中,离根节点最远的一个节点。
倍增算法
1.dfs一遍,创建ST表
2.利用ST表求LCA
内容来源 D09 倍增算法 P3379【模板】最近公共祖先(LCA)
#include<iostream>
#include<vector>
using namespace std;
#define MAX_N 500000
int n,m,s;
vector<int>e[MAX_N+5];
int dep[MAX_N+5];
int fa[MAX_N+5][20];
void dfs(int now,int father)
{dep[now]=dep[father]+1;fa[now][0]=father;//向上跳1,2,4...步的祖先节点 for(int i=1;i<=19;i++)fa[now][i]=fa[fa[now][i-1]][i-1];for(int v:e[now]){if(v==father)continue;dfs(v,now);}return ;
}
int lca(int u,int v)
{if(dep[u]<dep[v])swap(u,v);//先跳到同一层 for(int i=19;i>=0;i--)if(dep[fa[u][i]]>=dep[v])u=fa[u][i];if(u==v)return u;//再跳到lca的下一层 for(int i=19;i>=0;i--)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];return fa[u][0];
}
int main()
{cin>>n>>m>>s;for(int i=1,a,b;i<=n-1;i++){scanf("%d%d",&a,&b);e[a].push_back(b);e[b].push_back(a);}dfs(s,0);for(int i=1,a,b;i<=m;i++){scanf("%d%d",&a,&b);printf("%d\n",lca(a,b));}return 0;
}
Tarjan算法
内容来源 D10 Tarjan算法 P3379【模板】最近公共祖先(LCA)
#include<iostream>
#include<vector>
using namespace std;
#define MAX_N 500000
int n,m,s;
vector<int>e[MAX_N+5];
vector<pair<int,int>>query[MAX_N+5];
int fa[MAX_N+5];
int vis[MAX_N+5];
int ans[MAX_N+5];
int find(int x)
{return fa[x]=(fa[x]==x?x:find(fa[x]));
}
void tarjan(int u)
{vis[u]=1;//入u时,标记u for(auto v:e[u]){if(vis[v])continue;tarjan(v);fa[v]=u;//回u时,v指向u }for(auto q:query[u])//离u时,枚举lca {int v=q.first,t=q.second;if(vis[v])ans[t]=find(v);}return ;
}
int main()
{cin>>n>>m>>s;for(int i=1,a,b;i<=n-1;i++){scanf("%d%d",&a,&b);e[a].push_back(b);e[b].push_back(a);}for(int i=1,a,b;i<=m;i++){scanf("%d%d",&a,&b);query[a].push_back({b,i});query[b].push_back({a,i});}for(int i=1;i<=n;i++)fa[i]=i;tarjan(s);for(int i=1;i<=m;i++)printf("%d\n",ans[i]);return 0;
}
树链剖分算法
内容来源 D11 树链剖分 P3379【模板】最近公共祖先(LCA)
#include<iostream>
#include<vector>
using namespace std;
#define MAX_N 500000
int n,m,s;
vector<int>e[MAX_N+5];
int fa[MAX_N+5],sz[MAX_N+5],son[MAX_N+5],dep[MAX_N+5];
int top[MAX_N+5];
void dfs1(int u,int father)//搞fa,son,dep
{fa[u]=father;dep[u]=dep[father]+1;sz[u]=1;for(int v:e[u]){if(v==father)continue;dfs1(v,u);sz[u]+=sz[v];if(sz[son[u]]<sz[v])son[u]=v;}return ;
}
void dfs2(int u,int t)//搞top
{top[u]=t;//记录链头 if(!son[u])return ;//无重儿子 dfs2(son[u],t);//搜重儿子 for(int v:e[u]){if(v==fa[u]||v==son[u])continue;dfs2(v,v);//搜轻儿子 }return ;
}
int 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()
{cin>>n>>m>>s;for(int i=1,a,b;i<=n-1;i++){scanf("%d%d",&a,&b);e[a].push_back(b);e[b].push_back(a);}dfs1(s,0);dfs2(s,s);for(int i=1,a,b;i<=m;i++){scanf("%d%d",&a,&b);printf("%d\n",lca(a,b));}return 0;
}
总结
倍增算法 | T a r j a n Tarjan Tarjan算法 | 树链剖分 | |
---|---|---|---|
数据结构 | f a [ u ] [ i ] , d e p [ u ] fa[u][i],dep[u] fa[u][i],dep[u] | f a [ u ] , v i s [ u ] , q u e r y [ u ] , a n s [ i ] fa[u],vis[u],query[u],ans[i] fa[u],vis[u],query[u],ans[i] | f a [ u ] , d e p [ u ] , s z [ u ] , s o n [ u ] , t o p [ u ] fa[u],dep[u],sz[u],son[u],top[u] fa[u],dep[u],sz[u],son[u],top[u] |
算法 | 倍增法 深搜打标,跳跃查询 | 并查集 深搜,回时指父,离时搜根 | 重链剖分 两边深搜打表,跳跃查询 |
时间复杂度 | O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn) | O ( n + m ) O(n+m) O(n+m) | O ( n + m l o g n ) O(n+mlogn) O(n+mlogn) |