【模板】最近公共祖先(LCA)

定义

LCA,最近公共祖先,是指一棵树上两个节点的深度最大公共祖先。也可以理解为两个节点之间的路径深度最小的点。
我们这里用了倍增的方法求了LCA。
我们的基本的思路就是,用dfs遍历求出所有点的深度。f[i][j]数组用来求的是距离节点i,距离2^j的祖先。可以知道,f[i][0]就是它的直接父亲。然后通过倍增的思路求出father数组的所有元素。然后进行lca。求lca的基本思路是:先让深度较大的点向上跳,【模板】最近公共祖先(LCA)_第1张图片
然后x和y再同时向上跳2的幂,总会跳到这样两个点,他们的父亲结点是同一个点,那就是x和y的LCA。

首先我们需要用邻接表建一颗参天大树~

  • 重头戏——倍增。
int dep[maxn],f[maxn][64];
/*dep数组用来记录当前点的深度
f[i][j]代表距离i 2^j的祖先
*/
  • 深度和直接父亲:
void pre(int u,int fa)
{
    dep[u]=dep[fa]+1; //更新深度
    f[u][0]=fa;//更新父亲结点
    for(int i=1;(1<
  •  有的预处理工作都完成了。我们开始求LCA~
int lca(int x,int y)
{
    if(dep[x]=0;i--){//从大到小跳。正确性显然。
            if(f[x][i]!=f[y][i]){//如果不相等,就说明该节点的深度还是比LCA大
                x=f[x][i];
                y=f[y][i];
                //那就继续跳
            }
        }
        x=f[x][0];
        //这个时候x和y还不是同一个节点,但是x和y的父亲就是x和y的lca。
    }
    return x;
}
  • 常数优化:另外预处理出lg避免重复调用log函数
for(int i=1;i<=n;i++)
        lg[i]=lg[i-1]+(1<

P3379 【模板】最近公共祖先(LCA)

给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

对于100%的数据:N<=500000,M<=500000

 https://www.luogu.org/problemnew/show/P3379

#include
#include
#include
#include
using namespace std;
const int N=500001;
const int M=1000001;
int head[N],f[N][64],dep[N],lg[N];
int n,m,s,cnt;
struct node
{
	int next,to;
}e[M];
inline void add(int x,int y)
{
	e[++cnt].next=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
void pre(int u,int fa)
{
	dep[u]=dep[fa]+1;
	f[u][0]=fa;
	for(int i=1;(1<dep[y])
      x=f[x][lg[dep[x]-dep[y]]-1];
	if(x!=y){
		for(int i=lg[dep[x]];i>=0;i--){
			if(f[x][i]!=f[y][i]){
				x=f[x][i];
				y=f[y][i];
			}
		}
		x=f[x][0];
	}
	return x;
}
int main()
{
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1;i<=n-1;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	pre(s,0);
	for(int i=1;i<=n;i++)
        lg[i]=lg[i-1]+(1<

 

你可能感兴趣的:(总结与转载,图论-LCA)