倍增求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 }

 

posted @ 2018-07-31 21:21  DFSlover  阅读(1726)  评论(1编辑  收藏  举报

Contact with me