倍增LCA

前言

在做树上问题时,我们经常会遇到 LCALCALCA(最近公共祖先)问题。曾经的我遇到这类问题只会O(n)O(n)O(n)暴力求解,学了倍增LCALCALCA,就可以O(logn)O(logn)O(logn)解决了。


简介

倍增LCALCALCA,顾名思义,就是利用倍增来求解LCALCALCA(这真的是简介)。


主要思路
  • 我们可以用fa[i][j]fa[i][j]fa[i][j]来记录iii的第2j2^j2j个祖先。
  • 然后,对于每一次询问LCA(x,y)LCA(x,y)LCA(x,y),我们先找到xxxyyy最近的深度相同的祖先。
  • 接下来,我们先倍增找到最近的xxxyyy的刚好为2j2^j2j的公共祖先。
  • 然后,我们不断减小jjj,若当前fa[x][j]!=fa[y][j]fa[x][j]!=fa[y][j]fa[x][j]!=fa[y][j],我们就更新x=fa[x][j]x=fa[x][j]x=fa[x][j]y=fa[y][j]y=fa[y][j]y=fa[y][j],这样就可以保证修改后fa[x][j]=fa[y][j]fa[x][j]=fa[y][j]fa[x][j]=fa[y][j]了。
一个简短的证明

因为我们已经保证修改前的fa[x][j+1]=fa[y][j+1]fa[x][j+1]=fa[y][j+1]fa[x][j+1]=fa[y][j+1]
并且,显然可得,fa[fa[x][j]][j]=fa[x][j+1]fa[fa[x][j]][j]=fa[x][j+1]fa[fa[x][j]][j]=fa[x][j+1]fa[fa[y][j]][j]=fa[y][j+1]fa[fa[y][j]][j]=fa[y][j+1]fa[fa[y][j]][j]=fa[y][j+1]xxx的第2j2^j2j个祖先的第2j2^j2j个祖先即为xxx的第2j+12^{j+1}2j+1个祖先,yyy同理)
因此,修改后的fa[x][j]fa[x][j]fa[x][j]fa[y][j]fa[y][j]fa[y][j]就等同于修改前的fa[x][j+1]fa[x][j+1]fa[x][j+1]fa[y][j+1]fa[y][j+1]fa[y][j+1]
得证,我们可以保证修改后fa[x][j]=fa[y][j]fa[x][j]=fa[y][j]fa[x][j]=fa[y][j]

  • 最后返回fa[x][0]fa[x][0]fa[x][0]即可。

代码
inline int LCA(int x,int y)//求x和y的最近公共祖先
{
	register int i;int k;
	if(dep[x]<dep[y]) swap(x,y);//比较x和y的深度,选择深度较大的节点,寻找它的与深度较小的节点深度一样的祖先
	for(i=0;dep[x]^dep[y];++i) if((dep[x]^dep[y])&(1<<i)) x=fa[x][i];//如上
	if(!(x^y)) return x;//如果x==y,返回x
	for(k=0;fa[x][k]^fa[y][k];++k);//我们先倍增找到最近的x和y的刚好为2^j的公共祖先
	for(;k>=0;--k) if(fa[x][k]^fa[y][k]) x=fa[x][k],y=fa[y][k];//不断减小j,若当前fa[x][j]!=fa[y][j],我们就更新x=fa[x][j],y=fa[y][j]
	return fa[x][0];//最后返回x的父亲
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值