LCA(最近公共祖先)(2.14)
定义:LCA,即最近公共祖先,是指在在有根树中,找到两个节点的最近公共祖先。
如图,4和7的最近公共祖先是2。
如何求最近公共祖先:
1:两点同时网上走并标记,若第一次一个点走到被标记过得点,那个点就是最近公共祖先;
2:深度高的点先往上走,直到两点深度一样就一起往上走。
代码:。
3.倍增法:
注意到u,v走到最近公共祖先w之前,u,v所在结点不相同。而到达最近公共祖先w后,再往上走仍是u,v的公共祖先,即u,v走到同一个结点,这具有二分性质。于是可以预处理出一个2k的表,fa[k][u]表示u往上走2k步走到的结点,令根结点深度为0,则2k>depth[u]时,令fa[k][u]=-1(不合法情况的处理)。不妨假设depth[u] < depth[v]①将v往上走d = depth[v] - depth[u]步,此时u,v所在结点深度相同,该过程可用二进制优化。由于d是确定值,将d看成2的次方的和值d = 2k1 + 2k2 + ... + 2km,利用fa数组如v = fa[k1][v],v = fa[k2][v]加速。
②若此时u = v,说明Lca(u,v)已找到
③利用fa数组加速u,v一起往上走到最近公共祖先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],即u和v的父亲。
倍增如何预处理: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);
tarjan离线算法:
离线算法是把所有询问储存下来,一次处理全部,求得答案。tarjian算法就是这样,要找两个点a,b的最近公共祖先,先dfs从a点遍历到b点。遍历到b时,路劲上除了外节点的子树都遍历过了,而子树没遍历完,对于a,访问完他的子树后就在a的并查集中的父亲设为他在树中的父亲,访问b是b在a并查集中的父亲就是最a和b的近公共祖先。
这样依次更新,bfs,知道发现1诶呦被搜过的点也没有关系点就退出。
程序实现: