LAC 算法的定义:
对于树 T 的两个节点 u v满足最近公共祖先的 LAC(T,u,v) 存在一顶点 x 满足 x 是 u 和 v 的公共 祖先 ,且 x 的在树中深度最大。
LAC 的算法思想是:并查集 + dfs
详细过程代码:(给出详细的注解)
#include<iostream>
#include<vector>
using namespace std;
const int MAX=10001;
int f[MAX]; // 用于记录节点 的父节点
int r[MAX]; // 秩 用于 并查集时的优化其目的是在并查集中使得树的深度尽量最小。
int indegree[MAX]; //保存每个节点的入度
int visit[MAX]; // 注释 i 节点是否被访问
vector<int> tree[MAX],Qes[MAX];
int ancestor[MAX]; // ancertor[i] 其中 i 为集合的代号, ancertor[i] 为此集合的 Least Common Ancestor
void init(int n)
{
for(int i=1;i<=n;i++)
{
r[i]=1;
f[i]=i;
indegree[i]=0;
visit[i]=0;
ancestor[i]=0;
tree[i].clear();
Qes[i].clear();
}
}
int find(int n)
{
if(f[n]==n) {
return n;
}
else {
f[n]=find(f[n]);
}
return f[n];
}//查找函数,并压缩路径
int Union(int x,int y)
{
int a=find(x); // x 节点 所在 集合的代表
int b=find(y); // y 节点所在集合的代表
if(a==b) {
return 0;
}
//相等的话,x向y合并
else if(r[a]<=r[b])
{
f[a]=b;
r[b]+=r[a];
}
else
{
f[b]=a;
r[a]+=r[b];
}
return 1;
}//合并函数,如果属于同一分支则返回0,成功合并返回1
void LCA(int u)
{
ancestor[u]=u; // 记录 u 的最近祖先
int size = tree[u].size();// 得到 u 的 的子节点 并以递归 调用
for(int i=0;i<size;i++)
{
LCA(tree[u][i]); // 递归 调用
Union(u,tree[u][i]); // 当子节点 调用完成 将 节点 u, tree[u][i] 并日一个集合
ancestor[find(u)]=u; //记录 结合的最近祖先
}
visit[u]=1; // 标记 u 已经被访问
size = Qes[u].size();
for( i=0;i<size;i++)
{
//如果已经访问了 问题 节点,就可以返回结果了.
if(visit[Qes[u][i]]==1)
{
cout<<ancestor[find(Qes[u][i])]<<endl;
return;
}
}
}
int main()
{
int cnt;
int n;
cin>>cnt;
while(cnt--)
{
cin>>n;
init(n);
int s,t;
for(int i=1;i<n;i++)
{
cin>>s>>t; // 输入 2 个 想通的 顶点 组成边 (s ,t)
tree[s].push_back(t);
indegree[t]++; // 记录 t 的入度
}
//这里可以输入多组询问
cin>>s>>t; //
//相当于询问两次
Qes[s].push_back(t);
Qes[t].push_back(s);
for(i=1;i<=n;i++)
{
//寻找根节点 ( 可能存在多个 根节点)
if(indegree[i]==0)
{
LCA(i);
break;
}
}
}
return 0;
}