倍增法
变量
- int fa[i][j]:节点 i 的 2j 级祖先。
- int dep[i]:节点 i 的深度,即到根节点的距离。
- int lg[i]:log2i 向下取整的整数值。
函数
- LCA():求出 lg 数组。
- void dfs(int x,int f):计算子树 x 中的 fa 和 dep 数组,x 的父亲为 f。
- void init(int root):在树根为 root 时计算。
- int ask(int u,int v):求出 u 和 v 的最近公共祖先。
代码
struct LCA{
int fa[N][LOGN],dep[N];
int lg[N];
LCA(){
lg[0]=0;
for(int i=1;i<N;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
}
void dfs(int x,int f){
fa[x][0]=f;
dep[x]=dep[f]+1;
for(int i=1;i<=lg[dep[x]];i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=G.head[x];i;i=G.nxt[i])
if(G.ver[i]!=f)
dfs(G.ver[i],x);
}
void init(int root){
dfs(root,0);
}
int ask(int u,int v){
if(dep[u]<dep[v])
swap(u,v);
while(dep[u]>dep[v])
u=fa[u][lg[dep[u]-dep[v]]-1];
if(u==v)
return u;
for(int i=lg[dep[u]]-1;i>=0;i--)
if(fa[u][i]!=fa[v][i]){
u=fa[u][i];
v=fa[v][i];
}
return fa[u][0];
}
}tree;
Tarjan法
变量
- int v[i]:标记节点的访问状态,为 0 表示未访问过,为 1 表示访问过但未访问完其子树,为 2 表示访问过且访问完其子树。
- int lca[i]:询问 i 查询的 lca 结果。
- int fa[i]:并查集上 i 的父节点。
- vector<int> que[x]:和点 x 有关的询问的另一个节点。
- vector<int> qid[x]:和点 x 有关的询问的编号。
函数
- void init(int n):初始化 n 个节点的相关数组。
- void add_query(int x,int y,int id):添加询问求 x,y 的最近公共祖先。
- int get(int x):求并查集中 x 所在的集合的代表元素。
- void tarjan(int x):用 tarjan 法处理所有询问。
- void solve(int root):解决以 root 为树根时的答案。
代码
struct LCA{
int v[N],lca[N];
int fa[N];
vector<int> que[N],qid[N];
void init(int n){
memset(v,0,sizeof(v));
memset(lca,0,sizeof(lca));
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=n;i++){
que[i].clear();
qid[i].clear();
}
}
void add_query(int x,int y,int id){
if(x==y){
lca[id]=x;
return;
}
que[x].push_back(y);
qid[x].push_back(id);
que[y].push_back(x);
qid[y].push_back(id);
}
int get(int x){
if(x==fa[x])
return x;
return fa[x]=get(fa[x]);
}
void tarjan(int x){
v[x]=1;
for(int i=G.head[x];i;i=G.nxt[i]){
int y=G.ver[i];
if(v[y])
continue;
tarjan(y);
fa[y]=x;
}
for(int i=0;i<que[x].size();i++){
int y=que[x][i],id=qid[x][i];
if(v[y]==2)
lca[id]=get(y);
}
v[x]=2;
}
void solve(int root){
tarjan(root);
}
};