倍增求LCA
一、引言
定义
LCA,最近公共祖先,是指一棵树上两个节点的深度最大的公共祖先。也可以理解为两个节点之间的路径上深度最小的点。
我们这里用了倍增的方法求了LCA。
我们的基本的思路就是,用dfs遍历求出所有点的深度。f[i][j]数组用来求的是距离节点i,距离2^j的祖先。可以知道,f[i][0]就是它的直接父亲。然后通过倍增的思路求出father数组的所有元素。然后进行lca。求lca的基本思路是:先让深度较大的点向上跳,
然后x和y再同时向上跳2的幂,总会跳到这样两个点,他们的父亲结点是同一个点,那就是x和y的LCA。
首先我们需要用邻接表建一颗参天大树~
重头戏——倍增。
二、预处理
void Pretreatment(int u,int father) { deep[u]=deep[father]+1,fa[u][0]=father;//继承父亲的信息 for(int i=1,p=lg[deep[u]]-1;i<=p;i++) fa[u][i]=fa[fa[u][i-1]][i-1];//预先处理节点2^k次方祖先 for(int i=head[u];i;i=edge[i].next) if(edge[i].to!=father) Pretreatment(edge[i].to,u);//处理向下的其它树枝 }
三、倍增求LCA
int Lca(int from,int to) { if(deep[from]<deep[to]) Swap(from,to);//将from的深度设置为最深 while(deep[from]>deep[to]) from=fa[from][lg[deep[from]-deep[to]]-1];//倍增将x跳跃到与y同层 if(from==to) return from;//判断是否在同一个节点了 for(int i=lg[deep[from]]-1;i>=0;i--) if(fa[from][i]!=fa[to][i]) from=fa[from][i],to=fa[to][i];//倍增跳跃到LCA的下一层 return fa[from][0];//返回两者的LCA }
四、典例分析
#include<stdio.h> #include<stdlib.h> #include<math.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) using namespace std; const int N=500000,M=500000,P=20; int n,m,star,cnt,num_edge,head[N+1],fa[N+1][P+2],deep[N+1],lg[N+1]; //f[u][j]表示节点u向上2^j的祖先,deep[u]表示的是节点u的深度,lg[i]表示log2(i)+1; struct Edge{ int next,to; }edge[2*M+2]; void Add_edge(int from,int to){ edge[++num_edge]=(Edge){head[from],to},head[from]=num_edge; } inline void Swap(int &fa,int &fb){int t=fa;fa=fb,fb=t;} void Pretreatment(int u,int father) { deep[u]=deep[father]+1,fa[u][0]=father;//继承父亲的信息 for(int i=1,p=lg[deep[u]]-1;i<=p;i++) fa[u][i]=fa[fa[u][i-1]][i-1];//预先处理节点2^k次方祖先 for(int i=head[u];i;i=edge[i].next) if(edge[i].to!=father) Pretreatment(edge[i].to,u);//处理向下的其它树枝 } int Lca(int from,int to) { if(deep[from]<deep[to]) Swap(from,to);//将from的深度设置为最深 while(deep[from]>deep[to]) from=fa[from][lg[deep[from]-deep[to]]-1];//倍增将x跳跃到与y同层 if(from==to) return from;//判断是否在同一个节点了 for(int i=lg[deep[from]]-1;i>=0;i--) if(fa[from][i]!=fa[to][i]) from=fa[from][i],to=fa[to][i];//倍增跳跃到LCA的下一层 return fa[from][0];//返回两者的LCA } int main() { int from,to; scanf("%d%d%d",&n,&cnt,&star); int k=n-1; FORa(i,1,k) scanf("%d%d",&from,&to),Add_edge(from,to),Add_edge(to,from); FORa(i,1,n) lg[i]=lg[i-1]+(1<<lg[i-1]==i); Pretreatment(star,0);//预处理,最重要的优化,倍增跳 FORa(i,1,cnt) { scanf("%d%d",&from,&to); printf("%d\n",Lca(from,to));//求LCA } } /* 5 5 4 3 1 2 4 5 1 1 4 2 4 3 2 3 5 1 2 4 5 */
五、相关转载与推荐文章(十分感谢这些博主)