【模板】最近公共祖先(LCA)(倍增)
阅读须知:我认为读者已经掌握(或了解)了:
- 倍增思想
- 树(图)的基本概念及简单实现
- 存图与建图
- dfs
嗯,我们来看看最近公共祖先(LCA)的一种实现方式——倍增。
最近公共祖先
话说什么是最近公共祖先呢?emmm……大家如果知道树的话,应该就知道父亲节点与儿子节点了吧,那么祖先就是父亲的父亲的父亲的……;总之在同一条树链上,若x的深度小于y的深度,则称x是y的祖先。公共祖先,顾名思义就是两个点共同拥有的祖先;但是若x是y的祖先,则他们的共同祖先是x(很奇怪是不是)。最近公共祖先可以依字面意思理解,即“最近的”公共祖先。下面附图说明~
【图1】
如上图4和7的公共祖先有5、10,最近公共祖先为5;
3与20的公共祖先和最近公共祖先都是10;
2和3的公共祖先是3、5、10,最近公共祖先是3。
怎么样,对最近公共祖先是不是有些了解了。
下面我们再来看两张图~
【图2】
【图3】
请读者们仔细比对上面两张图,会发现,选择不同的根节点会使得两点之间有着不同的最近公共祖先。
倍增
关于实现LCA,想必大家都有暴力的思路,不断询问x与y的父亲节点,直到发现他们询问到相同的父亲节点位置,这当然会超时!所以,我们跳着查找【滑稽】,这就是倍增的思想。
我们使用一个数组lst[i][j]表示从i这个点向上跳2^j次所对应的点,即节点i的第2^j个父亲节点的编号。比如说上面第二个图中lst[5][0]=2,lst[5][1]=1。
请读者们细细体会下面的转化【很重要】(可以画一颗树试一试):
lst[son][i + 1] = lst[ lst[son][i] ][i]
倍增LCA的实现
1.建图(建议使用链式前向星)
2.预处理,更新lst数组
3.查询
下面分条作答
1.建图
链式前向星存图【不了解的话建议百度】
//建树一般不用存边权,建树一般要存双向边
struct EDGE
{
int to; //到达的点
int next; //上一条边
int w; //边权
}edge[2 * max_data]; //双向边
int edge_size = 0; //前向星数组模拟指针
int head[max_data]; //起点
void add(int u, int v, int w)
{
edge[++edge_size].next = head[u];
edge[edge_size].to = v;
edge[edge_size].w = w;