倍增求LCA算法详解
算法介绍:
看到lca问题(不知道lca是什么自(bang)行(ni)百度),不难想到暴力的方法;
先把两点处理到同一深度,再让两点一个一个祖先往上找,直到找到一个相同的祖先;
这么暴力的话,时间复杂度基本上是$ o(n) $;
而观察一下暴力的过程,就会发现,其实一个一个祖先往上找效率非常的低,有没有能优化这一过程的方法呢?这时,强大的倍增就出现了,能够把暴力优化到$ o(log(n)) $;
倍增,简单说就是把一步一步跳替换成每次跳$ 2^i $个祖先;
做法:
先预处理出每个点的深度(dfs或bfs),以及跳$ 2^i $个祖先后所在的位置(fa[i][j]表示第i个点跳$ 2^j $个祖先后的位置,再递推);
然后同理暴力,先把两点跳到同一深度(也用倍增),每次跳$ 2^i $个祖先,判断是否相等,如果相等就不跳(原因见易错点),否则跳;
难点:
1、递推时方程为fa[i][j]=fa[fa[i][j-1]][j-1],因为i跳$ 2^j $个祖先后所在的位置等于i连跳两次$ 2^{j-1} $个祖先后所在的位置;
2、递推时,j先扫1到20,i再扫1到n,因为每次更新f[i][j]要用到另外的位置跳$ 2^{j-1} $个祖先后所在的位置;
3、两个点跳的时候,如果相等,是不能直接输出的,有可能跳过头,就不是最近的公共祖先了;
4、每次读进来的边要存两次(树是无向图),同理,邻接表的数组也要开两倍长;
相关题目
1、洛谷p3379
模板题,放上丑陋的代码
1 #include<cstdio> 2 using namespace std; 3 const int MAXN=1000001;//两倍长 4 int tot,n,m,s,first[MAXN],last[MAXN],next[MAXN],to[MAXN],depth[MAXN],fa[MAXN][21]; 5 //depth是每个点的深度,fa[i][j]表示第i个点跳2^j个祖先后的位置 6 bool visited[MAXN]; 7 inline int read()//快读 8 { 9 int s=0,w=1; 10 char ch=getchar(); 11 while(ch<='0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} 12 while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); 13 return s*w; 14 } 15 void swap(int &x,int &y) 16 { 17 int k=x; 18 x=y; 19 y=k; 20 } 21 void add(int x,int y)//古董邻接表 22 { 23 ++tot; 24 if(first[x]==0) first[x]=tot; else next[last[x]]=tot; 25 last[x]=tot; 26 to[tot]=y; 27 } 28 void dfs(int now,int dep,int fat)//深搜求深度 29 { 30 if(visited[now]) return; 31 visited[now]=true; 32 depth[now]=dep; 33 fa[now][0]=fat;//要记得fa[i][0]存i的父亲(跳一(2^0)个祖先) 34 for(int i=first[now];i;i=next[i]) 35 { 36 dfs(to[i],dep+1,now); 37 } 38 } 39 int lca(int x,int y) 40 { 41 if(depth[x]<depth[y]) swap(x,y); 42 for(int i=20;i>=0;--i) 43 if(fa[x][i]!=0&&depth[fa[x][i]]>=depth[y]) 44 { 45 x=fa[x][i]; 46 } 47 if(x==y) return x; 48 for(int i=20;i>=0;--i) 49 if(fa[x][i]!=0&&fa[y][i]!=0&&fa[x][i]!=fa[y][i]) 50 { 51 x=fa[x][i]; 52 y=fa[y][i]; 53 } 54 return fa[x][0]; 55 } 56 int main() 57 { 58 n=read(); 59 m=read(); 60 s=read(); 61 for(int i=1;i<=n-1;++i) 62 { 63 int x,y; 64 x=read(); 65 y=read(); 66 add(x,y); 67 add(y,x); 68 } 69 dfs(s,1,0); 70 for(int j=1;j<=20;++j) 71 for(int i=1;i<=n;++i) 72 fa[i][j]=fa[fa[i][j-1]][j-1]; 73 for(int i=1;i<=m;++i) 74 { 75 int x; 76 int y; 77 x=read(); 78 y=read(); 79 printf("%d\n",lca(x,y)); 80 } 81 return 0; 82 }