最近公共祖先LCA(模板)
树上结点总数为n,那么借助于dfs可以实现O(n)预处理,O(n)计算任意两个结点 (u,v) 的LCA
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
int n;
vector<int> g[maxn]; //邻接表
int root; //树根
int parent[maxn]; //父亲结点,根结点的父亲为-1
int depth[maxn]; //结点深度
void dfs(int u,int fa,int d){
parent[u]=fa;
depth[u]=d;
for(int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v!=fa) dfs(v,u,d+1);
}
}
int LCA(int u,int v){ //计算u,v的最近公共祖先 O(n)
while(depth[u]>depth[v]) u=parent[u];
while(depth[v]>depth[u]) v=parent[v];
while(u!=v){
u=parent[u];
v=parent[v];
}
return u;
}
int main(){
while(scanf("%d",&n)==1 && n){
for(int i=0;i<n-1;++i){
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
root=1;
dfs(root,-1,0); //预处理 O(n)
int q;
scanf("%d",&q);
while(q--){
int u,v;
scanf("%d%d",&u,&v);
int ans=LCA(u,v);
printf("%d\n",ans);
}
}
return 0;
}
/*
8
1 2
1 3
2 4
2 5
3 6
5 7
5 8
*/
借助于二分搜索,可以实现 O(nlogn) 的预处理,然后在O(logn) 的时间内计算出任意两结点的LCA
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
const int max_logn=30;
int n; //树上的结点数(下标从0开始)
vector<int> g[maxn]; //邻接表
int root; //树根
int parent[max_logn][maxn]; //向上走2^k步所到达的结点(超过根时为-1)
int depth[maxn];
void dfs(int u,int fa,int d){
parent[0][u]=fa;
depth[u]=d;
for(int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v!=fa) dfs(v,u,d+1);
}
}
void init(){ //预处理 O(nlogn)
dfs(root,-1,0); //预处理parent[0]和depth
for(int k=0;k+1<max_logn;++k){
for(int u=0;u<n;++u){
if(parent[k][u]<0) parent[k+1][u]=-1;
else parent[k+1][u]=parent[k][parent[k][u]];
}
}
}
int LCA(int u,int v){ //计算LCA(u,v) O(logn)
if(depth[u]>depth[v]) swap(u,v);
for(int k=0;k<max_logn;++k){
if((depth[v]-depth[u])>>k&1){
v=parent[k][v];
}
}
if(u==v) return u;
for(int k=max_logn-1;k>=0;--k){
if(parent[k][u]!=parent[k][v]){
u=parent[k][u];
v=parent[k][v];
}
}
return parent[0][u];
}
int main(){
while(scanf("%d",&n)==1 && n){
for(int i=0;i<n-1;++i){
int u,v;
scanf("%d%d",&u,&v);
--u,--v;
g[u].push_back(v);
g[v].push_back(u);
}
root=0;
init();
int q;
scanf("%d",&q);
while(q--){
int u,v;
scanf("%d%d",&u,&v);
--u,--v;
int ans=LCA(u,v)+1;
printf("%d\n",ans);
}
}
return 0;
}
还有基于RMQ和dfs序的算法,O(nlogn)的预处理,O(1)计算
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
int n; //顶点个数(下标从0开始)
vector<int> g[maxn]; //邻接表
int root;
int vs[maxn<<1]; //dfs的访问顺序
int depth[maxn<<1]; //结点深度
int id[maxn]; //各个顶点在vs中首次出现的下标
int MinId[maxn<<1][30]; //最小值下标的S-T表
void RMQ_init(){
int len=n<<1;
for(int i=0;i<len;++i) MinId[i][0]=i;
for(int j=1;(1<<j)<=len;++j){
for(int i=0;i+(1<<j)-1<len;++i){
int x=depth[MinId[i][j-1]],y=depth[MinId[i+(1<<(j-1))][j-1]];
if(x<=y)
MinId[i][j]=MinId[i][j-1];
else
MinId[i][j]=MinId[i+(1<<(j-1))][j-1];
}
}
}
int RMQ_MinId(int L,int R){
int k=(int)(log((R-L+1)*1.0)/log(2.0));
if(depth[MinId[L][k]]<=depth[MinId[R-(1<<k)+1][k]])
return MinId[L][k];
else
return MinId[R-(1<<k)+1][k];
}
void dfs(int u,int fa,int d,int &k){
id[u]=k;
vs[k]=u;
depth[k++]=d;
for(int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v!=fa){
dfs(v,u,d+1,k);
vs[k]=u;
depth[k++]=d;
}
}
}
void init(){ //预处理
int k=0;
dfs(root,-1,0,k); //处理vs,depth,id
RMQ_init(); //建立S-T表
}
int LCA(int u,int v){
int a=min(id[u],id[v]);
int b=max(id[u],id[v]);
return vs[RMQ_MinId(a,b)];
}
int main(){
while(scanf("%d",&n)==1 && n){
for(int i=0;i<n-1;++i){
int u,v;
scanf("%d%d",&u,&v);
--u,--v;
g[u].push_back(v);
g[v].push_back(u);
}
root=0;
init();
int q;
scanf("%d",&q);
while(q--){
int u,v;
scanf("%d%d",&u,&v);
--u,--v;
int ans=LCA(u,v)+1;
printf("%d\n",ans);
}
}
return 0;
}