最近公共祖先

前置芝士

倍增算法

本质思想:可以通过二进制数任意组合拼凑出任意一个正整数

最近公共祖先(LCA)

[problem description]

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

[input]

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

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

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

[output]

输出包含 \(M\) 行,每行包含一个正整数,依次为每一个询问的结果。

[sample]

in

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

out

4
4
1
4
4

\(1 \leq N,M\leq 500000\)\(1 \leq x, y,a ,b \leq N\)不保证 \(a \neq b\)

[solved]

倍增法求解

const int N=500010;
int dep[N],pa[N][22];
vector<int> e[N];
int n,m,s,a,b;
void dfs(int x,int fa){
	dep[x]=dep[fa]+1;
	pa[x][0]=fa;
	for(int i=1;i<=20;i++){
		pa[x][i]=pa[pa[x][i-1]][i-1];
	}
	for(int y:e[x]){
		if(y!=fa) dfs(y,x);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--){
		if(dep[pa[x][i]]>=dep[y]) x=pa[x][i];
	}
	if(x==y) return y;
	for(int i=20;i>=0;i--){
		if(pa[x][i]!=pa[y][i]) x=pa[x][i],y=pa[y][i];
	}
	return pa[x][0];
}
void solve() {
	cin>>n>>m>>s;
	for(int i=1,x,y;i<n;i++){
		cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	dfs(s,0);
	for(int i=1,x,y;i<=m;i++){
		cin>>x>>y;
		cout<<lca(x,y)<<endl;
	}
}

Tarjan(塔扬)

是一种离线算法,巧妙利用并查集维护祖先节点

posted @ 2023-11-01 23:08  White_Sheep  阅读(8)  评论(0编辑  收藏  举报