【模板】倍增求LCA

题目链接

一、 时间戳法(本质上是dfs序)

#include<cstdio>
using namespace std;
const int NN = 5e5+8;
int n,m,s;
int tin[NN],tout[NN],timetable;
int fa[NN][30];

struct Edge{
	int next,to;
}edge[NN<<1];
int head[NN],cnt;
void init(){
	cnt = 1;
	for(int i = 1; i <= n; i++)head[i] = -1;
}
void add_edge(int u,int v){
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	head[u] = cnt;
}//链式前向星

void dfs(int now,int father){
	fa[now][0]=father;
	tin[now] = ++timetable;
	for(int j = 1; j <= 29 ; j++){
		fa[now][j] = fa[fa[now][j-1]][j-1];
	}
	for(int i = head[now]; i != -1; i = edge[i].next){
		int tt = edge[i].to;
		if(father != tt)dfs(tt,now);
	}
	tout[now] = ++timetable;
	return;
}//一遍dfs预处理fa数组,时间戳

bool isfather(int a,int b){
	return tin[a]<tin[b]&&tout[b]<tout[a];
}//可以知道先进入dfs后退出的一定是更先搜到
int LCA(int x,int y){
	if(x==y)return x; 
	if(isfather(x,y))return x;
	if(isfather(y,x))return y;
	int now = x;
	for(int i = 29; i >= 0; i--){
		if(!isfather(fa[now][i],y))now = fa[now][i];
	}
	return fa[now][0];
} 
int main(){
	scanf("%d%d%d",&n,&m,&s);
	init();
	for(int i = 1,u,v; i < n; i++){
		scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs(s,s);
	while(m--){
		int u,v;
		scanf("%d%d",&u,&v);
		printf("%d\n",LCA(u,v));
	}
	return 0;
}

码量:1228
在这里插入图片描述

二、 普通法(先跳到同一深度,再向上跳)

#include<cstdio>
#include<algorithm> 
using namespace std;
const int NN = 5e5+8;
int n,m,s;

int fa[NN][30],dep[NN];
struct Edge{
	int next,to;
}edge[NN<<1];
int head[NN],cnt;
void init(){
	cnt = 1;
	for(int i = 1; i <= n; i++)head[i] = -1;
}
void add_edge(int u,int v){
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	head[u] = cnt;
}//链式前向星

void dfs(int depth,int now,int father){
	fa[now][0] = father;
	dep[now] = depth;
	for(int i = 1; i <= 29; i++){
		fa[now][i] = fa[fa[now][i-1]][i-1];
	}
	for(int i = head[now]; i != -1; i = edge[i].next){
		int tt = edge[i].to;
		if(tt != father)dfs(depth+1,tt,now);
	}
}//一遍dfs处理fa数组

int LCA(int x,int y){
	if(dep[y]>dep[x])swap(x,y);
	int t = dep[x]-dep[y];
	for(int i = 0; t; i++){
		if(t&1)x = fa[x][i];
		t>>=1;
	}//跳到同一深度
	if(x==y)return x;
	for(int i = 29; i >= 0; i--){
		if(fa[x][i] != fa[y][i])x = fa[x][i],y = fa[y][i];
	} //倍增
	return fa[x][0];
}

int main(){
	scanf("%d%d%d",&n,&m,&s);
	init();
	for(int i = 1,u,v; i < n; i++){
		scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs(1,s,s);
	while(m--){
		int u,v;
		scanf("%d%d",&u,&v);
		printf("%d\n",LCA(u,v));
	}
	return 0;
}

码量:1179
在这里插入图片描述
两种代码自取

posted @ 2023-01-13 15:56  ricky_lin  阅读(17)  评论(0编辑  收藏  举报