模板: 最近公共祖先(倍增实现) LCA-Doubling

Lowest Common Ancestors (LCA)-Doubling

最近公共祖先(倍增实现)

模板题

Brief Introduction

在图论和CS中,两个节点\(v\)\(w\)在树上或者有向无环图中的最近公共祖先(LCA)\(T\)是指,同时拥有\(v\)\(w\)这两个节点作为后代的最深的节点。我们定义每个节点也是其本省的后代(所以如果\(v\)\(w\)的后代,\(w\)就是它们的最近公共祖先)

In graph theory and computer science, the lowest common ancestor (LCA) of two nodes v and w in a tree or directed acyclic graph (DAG) T is the lowest (i.e. deepest) node that has both v and w as descendants, where we define each node to be a descendant of itself (so if v has a direct connection from w, w is the lowest common ancestor).

Algorithm

本文介绍求LCA在线算法中的倍增实现,初始化复杂度\(O(n)\),单次查询复杂度\(O(\log n)\)

算法的核心在于ST表,用 \(\text S\text T[cur][i]\) 数组存放 \(cur\) 节点的第\(2^i\)辈祖先。

用DFS从根遍历初始化,打表转移方程\(\text S\text T[cur][i]=\text S\text T[\text S\text T[cur][i-1]][i-1]\)。 复杂度\(O(\log n)\)

询问时,先将\(u\)\(w\)某一个更深的点,利用ST表,向祖先移动至同样深度。

如果此时\(u=w\)则该节点便是 LCA,

否则同样深度下\(u \not=w\),则用ST表从最深的深度递减试探,将\(u,w\)在ST表移动至最浅的祖先在同样深度下,\(u\not=w\)的节点,它们的父节点便是所求LCA。

Template Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 500005
#define MAXM 500005
#define MAXLN 25
using namespace std;

struct edge{
	int to,next;
}e[MAXM<<1];

int tot,head[MAXN];

void add(int x,int y){
	tot++;
	e[tot].to=y;
	e[tot].next=head[x];
	head[x]=tot;
}

int dep[MAXN],lgd[MAXN],st[MAXN][MAXLN];

void dfs(int cur,int fa){
	dep[cur]=dep[fa]+1;
	st[cur][0]=fa;
	for(lgd[cur]=1;(1<<lgd[cur])<=dep[cur];lgd[cur]++)
		st[cur][lgd[cur]]=st[st[cur][lgd[cur]-1]][lgd[cur]-1];

	for(int p=head[cur];p;p=e[p].next){
		if(e[p].to==fa) continue;
		dfs(e[p].to,cur);
	}
}

int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=0;dep[x]-dep[y];i++)
		if((dep[x]-dep[y])&(1<<i)) x=st[x][i];
	if(x==y) return x;
	
	for(int i=lgd[x];i>=0;i--)
		if(st[x][i]!=st[y][i])
			x=st[x][i], y=st[y][i];
	return st[x][0];
}

int n,m,s;

int main(){
	scanf("%d %d %d", &n, &m, &s);
	tot=0;
	memset(head+1,0,n*sizeof(head[0]));
	for(int i=1;i<n;i++){
		int f,g;
		scanf("%d %d", &f, &g);
		add(f,g);
		add(g,f);
	}
	dep[0]=0;
	dfs(s,0);
	for(int i=1;i<=m;i++){
		int f,g;
		scanf("%d %d", &f, &g);
		printf("%d\n", lca(f,g));
	}
	return 0;
}
posted @ 2020-02-25 16:03  Leachim  阅读(191)  评论(0编辑  收藏  举报