[笔记]倍增求LCA

[笔记]倍增求LCA

原题链

算法描述

LCA是指两个点的最近公共祖先.

在程序中设f[x][k]表示x2^k 辈祖先,即从x向上(根节点)走 2^k 步所到达的节点,如果向上走到的节点不存在则令f[x][k] = 0,并且f[x][0]为x节点的父节点,因为 2^0 = 1,且任意一个节点向上走一步到达的就是它的父节点.同时我们会发现一个性质:∀k∈[1,log(n)],f[x][k] = f[f[x][k - 1][k - 1]],这个式子的意思是x向上跳 2^k 步所到达的节点与x向上跳 2^(k-1) 步所到达的节点再向上跳 2^(k-1) 步所到达的节点是相同的,可以用同底数幂相加的原理证明(a^b + a^c = a^(b+c)).以上为预处理部分.


如何求LCA:设dis[x]为x节点的深度,并规定dis[x] ≥ dis[y]如果不满足这个条件则交换x,y;此时我们保证了x的深度一定大于y,所以我们先尝试将x向上跳 2^(logn) 步,…跳 2^1 步,跳 2^0 步,并检查所跳到的节点是否仍比y深,如果仍比y深则继续上跳.如果调到x==y则说明x,y已经到达了两者的LCA处,直接输出.但如果当两者的深度一致时x,y仍不相等,此时将x,y同时上跳直到f[x][i] != f[y][i],由于我们跳的步幅是从大到小的,所以一开始跳可能回调到两点的lca的祖先节点,这很明显不是答案,所以我们要缩小步伐,缩小到什么时候呢?应该是第一次两点向上跳到的节点不是同一个的时候,我们设这个节点为p,此时p节点的父节点(这个父节点不是通常意义上的高一级的直属父亲,而是倍增意义下跳 2^i 步到达的节点)即为x,y的LCA.算法结束,具体见程序

AC代码

#include <bits/stdc++.h>
using namespace std;
struct node{
	int to,next;
}edge[1000010];
int fir[1000010],n,m,s,tot,t,dis[500010],f[500010][35];
void add(int x,int y){//前向星存边
	tot++;
	edge[tot].to = y;
	edge[tot].next = fir[x];
	fir[x] = tot;
}
void bfs(){//预处理出每个点的深度,并求出f数组
	memset(dis,0,sizeof(dis));
	queue < int > q;
	while(!q.empty())q.pop();
	dis[s] = 1;
	q.push(s);
	while(!q.empty()){
		int x = q.front();
		q.pop();
		for(int i = fir[x];i;i = edge[i].next){
			if(dis[edge[i].to] != 0)continue;
			f[edge[i].to][0] = x;
			dis[edge[i].to] = dis[x] + 1;
			for(int j = 1;j <= t;j++){
				f[edge[i].to][j] = f[f[edge[i].to][j - 1]][j - 1];
			}
			q.push(edge[i].to);
		}
	}
	return;
}
int lca(int x,int y){
	if(dis[x] < dis[y])swap(x,y);
	if(x == y)return x;
	for(int i = t;i >= 0;i--){
		if(dis[f[x][i]] >= dis[y])x = f[x][i];
	}
	if(x == y)return x;
	for(int i = t;i >= 0;i--){
		if(f[x][i] != f[y][i]){
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	for(int i = 1;i < n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	t = (int)(log(n) / log(2)) + 1;
	bfs();
	for(int i = 1;i <= m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));
	}
	return 0;
}

结束

posted @ 2020-08-22 16:40  czyczy  阅读(186)  评论(0编辑  收藏  举报