LCA即最近公共祖先,是指 :在有根树中,找出某两个结点u和v最近的公共祖先。
时间复杂度O(nlogn+m+n)
步骤:
1.将树看作一个无向图,从根节点开始深搜,得到一个遍历序列。
2.在x~y区间中利用RMQ算法找到深度最小返回其下标。
可以上洛谷找模板题测试:https://www.luogu.org/problemnew/show/P3379
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=500010;
struct node
{
int v,ne;
}q[N*2];
int f[N],vis[N],deep[N*2],pos[N*2];
int dp[N*2][21];
int cut,n,e;
void add(int a,int b)
{
q[++e].v=b;
q[e].ne=f[a];
f[a]=e;
return;
}
void dfs(int x,int s,int pre)
{
vis[x]=++cut;
pos[cut]=x;
deep[cut]=s;
for(int i=f[x];i!=0;i=q[i].ne)
{
int v=q[i].v;
if(v==pre) continue;
dfs(v,s+1,x);
pos[++cut]=x;
deep[cut]=s;
}
return;
}
void RMQ()
{
for(int i=1;i<=cut;i++)
dp[i][0]=i;
for(int i=1;(1<<i)<=cut;i++)
{
for(int j=1;j+(1<<i)-1<=cut;j++)
{
int a=dp[j][i-1];
int b=dp[j+(1<<(i-1))][i-1];
if(deep[a]<=deep[b])
dp[j][i]=a;
else dp[j][i]=b;
}
}
return;
}
int query(int x,int y)
{
int k=0;
k=log2(double(y-x+1));
int a=dp[x][k],b=dp[y-(1<<k)+1][k];
if(deep[a]<=deep[b])
return pos[a];
else
return pos[b];
return 0;
}
int main()
{
cut=0;e=0;
int m,x;
scanf("%d %d %d",&n,&m,&x);
int a,b;
for(int i=1;i<n;i++)
{
scanf("%d %d",&a,&b);
add(a,b);
add(b,a);
}
dfs(x,0,-1);
RMQ();
for(int i=0;i<m;i++)
{
scanf("%d %d",&a,&b);
printf("%d\n",query(min(vis[a],vis[b]),max(vis[a],vis[b])));
}
return 0;
}
倍增法求LCA:
复杂度 :预处理O(nlogn),每次询问O(logn)
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=5e5+10;
struct node
{
int v,ne;
}q[N*2];
int deep[N],par[N][20],f[N],vis[N];
int cut,e;
void add(int a,int b)
{
q[++e].v=b;
q[e].ne=f[a];
f[a]=e;
}
void dfs(int x,int pre,int s)
{
deep[x]=s;
for(int i=f[x];i!=0;i=q[i].ne)
{
int v=q[i].v;
if(v==pre) continue;
par[v][0]=x;
dfs(v,x,s+1);
}
}
void prepare(int n)
{
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i<=n;i++)
{
if(par[i][j-1]!=-1)
par[i][j]=par[par[i][j-1]][j-1];
}
}
}
int lca(int a,int b)
{
if(deep[a]<deep[b]) swap(a,b);
int k=trunc(log2(deep[a]));
for(int i=k;i>=0;i--)
{
if(deep[a]-(1<<i)>=deep[b])
a=par[a][i];
}
if(a==b) return a;
for(int i=k;i>=0;i--)
{
if(par[a][i]!=par[b][i]&&par[a][i]!=-1)
{
a=par[a][i];
b=par[b][i];
}
}
return par[a][0];
}
int main()
{
memset(par,-1,sizeof(par));
int n,m,x,a,b;
scanf("%d %d %d",&n,&m,&x);
for(int i=1;i<n;i++)
{
scanf("%d %d",&a,&b);
add(a,b);
add(b,a);
}
dfs(x,0,0);
prepare(n);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&a,&b);
printf("%d\n",lca(a,b));
}
return 0;
}