LCA(最近公共祖先)(2.14)

定义:LCA,即最近公共祖先,是指在在有根树中,找到两个节点的最近公共祖先。

如图LCA(最近公共祖先)(2.14),4和7的最近公共祖先是2。

如何求最近公共祖先:

1:两点同时网上走并标记,若第一次一个点走到被标记过得点,那个点就是最近公共祖先;

2:深度高的点先往上走,直到两点深度一样就一起往上走。

代码:LCA(最近公共祖先)(2.14)

3.倍增法:

注意到uv走到最近公共祖先w之前,uv所在结点不相同。而到达最近公共祖先w后,再往上走仍是uv的公共祖先,即uv走到同一个结点,这具有二分性质。于是可以预处理出一个2k的表,fa[k][u]表示u往上走2k步走到的结点,令根结点深度为0,则2k>depth[u]时,令fa[k][u]=-1(不合法情况的处理)。不妨假设depth[u] < depth[v]①将v往上走d = depth[v] - depth[u]步,此时uv所在结点深度相同,该过程可用二进制优化。由于d是确定值,将d看成2的次方的和值d = 2k1 + 2k2 + ... + 2km,利用fa数组如v = fa[k1][v]v = fa[k2][v]加速。

②若此时u = v,说明Lca(u,v)已找到

③利用fa数组加速uv一起往上走到最近公共祖先w的过程。令d = depth[u] - depth[w],虽然d是个未知值,但依然可以看成2的次方的和。从高位到低位枚举d的二进制位,设最低位为第0位,若枚举到第k位,有fa[k][u] != fa[k][v],则令u = fa[k][u]v = fa[k][v]。最后最近公共祖先w = fa[0][u] = fa[0][v],即uv的父亲。

倍增如何预处理:k=0时,fa[k][u]为u在有根树的父亲,令fa[k][root]=-1;k>0时,fa[k][u]=fa[k-1][fa[k-1][u]];

时间复杂度:预处理o(nlogn);单词查询o(longn);LCA(最近公共祖先)(2.14)

LCA(最近公共祖先)(2.14)

tarjan离线算法:

离线算法是把所有询问储存下来,一次处理全部,求得答案。tarjian算法就是这样,要找两个点a,b的最近公共祖先,先dfs从a点遍历到b点。遍历到b时,路劲上除了外节点的子树都遍历过了,而子树没遍历完,对于a,访问完他的子树后就在a的并查集中的父亲设为他在树中的父亲,访问b是b在a并查集中的父亲就是最a和b的近公共祖先。

LCA(最近公共祖先)(2.14)

这样依次更新,bfs,知道发现1诶呦被搜过的点也没有关系点就退出。

程序实现:

LCA(最近公共祖先)(2.14)