LCA 与树上差分
Posted 未欣的 blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCA 与树上差分相关的知识,希望对你有一定的参考价值。
概述
-
LCA(least common ancestor),最近公共祖先的英文缩写。
-
顾名思义,LCA 就是树上两个点最近的公共祖先,或者说两个点共同在的极小子树的根。
-
树上差分则是利用树本身的结合性(显然树不满足差分性,是点到根的链满足差分性)与 LCA 结合做的操作,譬如给某个路径上所有点 \\(+x\\),转换到差分意义下后等价于路径的左右端点分别 \\(+x\\),LCA 和 LCA 的父亲处分别 \\(-x\\)。
-
当然也可以是对边操作之类的,不过具体情况具体分析即可。
求 LCA
tarjan 求 LCA
-
tarjan 求 LCA 是一种离线求 LCA 的方式。主要利用“共同所在极小子树”这一性质,按欧拉环游序处理点的信息,结合并查集求 LCA。
-
具体来讲,就是从根节点开始 dfs ,进行如下操作:
-
递归遍历 \\(u\\) 所有儿子 \\(v\\)。全部遍历后标记 \\(u\\) 已被访问过,并将 \\(u\\) 合并到 \\(fa\\) 上。
-
枚举所有要与 \\(u\\) 求 LCA 的点,如果对应点已访问过,则两点 LCA 为对应点的 \\(FA\\)(并查集意义下)。
-
-
证明:利用欧拉环游序。
-
可达性:当第二个点开始更新答案的时候,两点同在的极小子树一定还没有被 dfs 完。从而该子树的根还没有出栈,没有合并给它自己的父亲,find 一定会卡在它这里。
-
必达性:假设 find 到了极小子树根的某个子孙节点,则该节点还没有出栈,故该节点的子树没有 dfs 完,故当前节点也是该子树的节点,该子树才是极小子树。假设不成立。
-
-
注意到这里我们的并查集是定向合并的,所以在路径压缩的前提下,最坏情况 \\(O(n\\log n)\\)。tarjan 的 \\(O(n\\alpha)\\) 证明好像是基于树分块的。
-
给出示范代码(好久之前的东西了)。
int FA[maxn]; bool tarvis[maxn];
il int find(int x)return x==FA[x]?x:FA[x]=find(FA[x]);
il void tarjan(int now,int fa)
for(int to:e[now])
if(to!=fa)
tarjan(to,now);
tarvis[now]=1,FA[now]=FA[fa];
for(qlca qn:ql[now])
if(tarvis[qn.with])
p[qn.id].lca=find(qn.with);
return;
倍增求 LCA
-
倍增求 LCA 是一种预处理每个点的 \\(2\\) 的整数次幂级祖先后,在线求 LCA 的方式。
-
建一个倍增数组 \\(fa2_step,x\\) (\\(step\\) 一般用到 \\(20\\),因为 \\(2^20=1048576>10^6\\)),表示从 \\(x\\) 向上走 \\(2^step\\) 步后到哪里。可以认为是一种反边。
-
具体实现的话,先 dfs 预处理出 \\(fa2_0,x=fa\\)。
-
然后按倍增性质模仿 dp 做一下,转移式:\\(fa2_step,i=fa2_step-1,fa2_step-1,i\\),即先走 \\(2^step-1\\) 步,再走 \\(2^step-1\\) 步。
-
询问的时候先让较深的点跳到同深处。如果发现两点已经相同,说明共链,应当直接
return
。 -
之后进行倍增二分,从大到小枚举 \\(step\\),若 \\(fa2_step,u\\neq fa2_step,v\\),则令 \\(u=fa2_step,u,v=fa2_step,v\\),可以认为是把需要走的步数二进制拆分了。
-
最后额外走一步,因为该倍增二分找的其实是最浅的非 lca 点。这里仅给出求 lca 函数的实现,预处理的话不好封装。
int lca(int u,int v)
if(dep[u]<dep[v]) swap(u,v);
int cha=dep[u]-dep[v];
foR(i,19,0)
if(cha&(1<<i)) u=FA[i][u];
if(u==v) return u;
foR(i,19,0)
if(FA[i][u]!=FA[i][v]) u=FA[i][u],v=FA[i][v];
return FA[0][u];
-
之所以 \\(\\log\\) 在前是因为据说这样比较快(两三倍常数吧),考虑到访问连续性,确实值得这么做。
-
upd:实测总是确实较快,但为了最好的效果,不可放在 dfs 内求,而应拉出来用循环,以 step 为层求。
重剖求 LCA
- 参见“轻重链剖分”。
以上是关于LCA 与树上差分的主要内容,如果未能解决你的问题,请参考以下文章