LCA-ST算法&Tarjan_LCA

来源:互联网 发布:奥特歌词软件 编辑:程序博客网 时间:2024/04/29 07:53

定义

树中两个节点x,y的最近公共祖先(Lowest Common Ancestors,简称LCA)指的是既是x祖先,又是y祖先且距离x和y最近的节点。

ST算法(在线)

我们有一个求x和y的LCA的初始想法:就是先让x和y中dep较大的节点与dep较小的节点处在同一深度,然后同时向上走,直到相遇,此时相遇的节点即为LCA:
这里写图片描述
复杂度为O(nQ),并不是很好,我们需要优化。
初始想法很明显的一个缺陷是:每次只走一层,太慢了,而这和RMQ初始想法的缺陷是一样的。所以我们会想到ST算法!记录fa[i][j]表示i向上走2^j次到达的节点(我们认为根向上走若干次会到达自己),那么很容易得到转移:
fa[i][j]=fa[fa[i][j-1]][j-1]
ps:我们还可以类似的记录其他信息,比如MAX[i][j]表示i向上走2^j次经过的边(点)权的最大值,从而求出一条路径中边(点)权的最大值。

构造好fa之后,如何求x到y的LCA呢?如下(不妨设dep[x]>dep[y]):
1.从大到小枚举i(log2(dep[x]dep[y])~0),如果dep[fa[x][i]]>=dep[y],那么令x=fa[x][i]。由于这样可以视为枚举二进制位,所以处理完毕之后必定有dep[x]=dep[y]。
2.如果x=y,则x(y)即为LCA,结束。
3.从大到小枚举i(log2dep[x]~0),如果fa[x][i]!=fa[y][i],那么令x=fa[x][i],y=fa[y][i]。处理完毕之后x(y)再向上走一次必定就是LCA,原理同1。
4.令x=fa[x][0],此时的x即为LCA。
复杂度为O((n+Q)log2n)

Tarjan_LCA(离线)

求LCA还有个离线算法Tarjan,Tarjan的想法是先把所有询问存下来,然后一次性全部处理,所以是离线的。过程如下:
1.用DFS遍历这棵树(优先处理出子树)。
2.假设目前遍历的点是x,子树son已经处理完毕,那么将x与son用并查集合并。
3.枚举与x有关的询问(x,y),如果y已经访问过,那么LCA(x,y)=getfa(y)即y目前的祖先。

这里简要说明一下为什么LCA(x,y)=getfa(y):
这里写图片描述
由于y已经访问过,根据深度优先搜索DFS的性质,一定有dep[y]>=dep[x]。
因为y在x之前被访问,所以y必定已经和LCA处于同一个连通块中。而且x还没处理完毕,意味着LCA也没处理完毕,所以LCA必定是这个连通块的祖先。

还有一个小问题,就是我们怎么保证y已经访问过了呢?如果没有访问过,不是无法处理出LCA了?所以我们对于一个询问(x,y),还需要添加询问(y,x),这样就可以方便的解决这个问题了。

由于并查集路径压缩之后可以视为常数,所以复杂度为O(n+Q)

模板题

HDU2586,题解传送门。

原创粉丝点击