倍增法求LCA——在线

Posted E-Valley

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了倍增法求LCA——在线相关的知识,希望对你有一定的参考价值。

 

推荐 B站视频讲解

首先我们考虑暴力做法:

  分别从两个点开始一层一层地向上跳,直到跳到一样的点,这个点就是他俩的LCA了。

这个做法实在太暴力了,不可取,不可取. . .

 

于是我们找到了一个不那么暴力的做法——倍增法。

我们可以用倍增来在线求LCA(其(da)实(gai)就是一跳跳好几层)。

 

fa[i][j] 记录从 i 点向上跳 2j 层所到达的点,fa数组是预处理得出的(还有每个点的深度deep也是),后面会说

_____________先看LCA的求法____________________________________________

我们知道对于两个点的位置有两种情况:

① 位于同一层:

  这时只要两个点一起跳,直到跳到相同的点即可。

for (int i = 20; i >= 0; i--) {
	if (fa[x][i] != fa[y][i]) {
		x = fa[x][i];
		y = fa[y][i];
	}
}

② 不同层:
  将深度更深的点向上跳到与另一个点同一层,即转化为①。

if (node[x].deep < node[y].deep) {
	int t = x;                      // 保证 x 是更深的那个点
	x = y;
	y = t;
}	
	
for (int i = 0; i <= 20; i++) {
	if ((node[x].deep - node[y].deep)>> i & 1) x = fa[x][i];
}
	
if (x == y) return x;                    // 特判:两个点在同一条链上的情况(即:它们其中一个点就是它俩的LCA)

 

______________预处理__________________________________________________

一个 dfs 搞定(从根结点开始DFS)。

void dfs(int s) {	
	for (int k = 1; k <= 20; k++)							 
        fa[s][k] = fa[fa[s][k - 1]][k - 1];
	
	for (Edge *e = node[s].last; e; e = e->next) {
		if (!e->to->deep) {
			e->to->deep = node[s].deep + 1;
			fa[e->to->ma][0] = node[s].ma;
			dfs(e->to->ma);
		} 
	}
}

 

完整代码:

 例题codevs 1036 商务旅行

#include <cstdio>

const int MAXN = 30007;

int n, m, fa[MAXN][30], a[MAXN], ans;

/**********************快********************************/
int read() {
	int x = 0;
	char ch = getchar();
	
	while (ch < ‘0‘ || ch > ‘9‘) ch = getchar();
	while (ch >= ‘0‘ && ch <= ‘9‘) x = x * 10 + ch - ‘0‘, ch = getchar();
	
	return x;
}
/************************读******************************/

struct Edge;
struct Node {
	Edge *last;
	int deep, ma;
	bool vis;
} node[MAXN];

struct Edge {
	Node *from, *to;
	Edge *next;
	Edge (Node *from, Node *to) : from(from), to(to), next(from->last) {}
};

void addE(int x, int y) {
	node[x].last = new Edge(node + x, node + y);
	node[y].last = new Edge(node + y, node + x);
}

void dfs(int s) {	
	for (int k = 1; k <= 20; k++)							// 注意!!!! 
        fa[s][k] = fa[fa[s][k - 1]][k - 1];
	
	for (Edge *e = node[s].last; e; e = e->next) {
		if (!e->to->deep) {
			e->to->deep = node[s].deep + 1;
			fa[e->to->ma][0] = node[s].ma;
			dfs(e->to->ma);
		} 
	}
}

int lca (int x, int y) {
	if (node[x].deep < node[y].deep) {
		int t = x;
		x = y;
		y = t;
	}	
	
	for (int i = 0; i <= 20; i++) {
		if ((node[x].deep - node[y].deep)>> i & 1) x = fa[x][i];
	}
	
	if (x == y) return x;
	
	for (int i = 20; i >= 0; i--) {
		if (fa[x][i] != fa[y][i]) {
			x = fa[x][i];
			y = fa[y][i];
		}
	}
	
	return fa[x][0];
}

int main() {
	n = read(); 

	for (int i = 1; i <= n; i++) node[i].ma = i;

	for (int i = 1, b, c; i < n; i++) {
		c = read();
		b = read();
		addE(c, b);
	}
	
	fa[1][0] = 1;
	node[1].deep = 1;
	dfs(1);

	m = read();
	for (int i = 1; i <= m; i++) {
		a[i] = read();
	}

	for (int i = 2; i <= m; i++) {
		ans += node[a[i]].deep + node[a[i - 1]].deep - 2 * node[lca(a[i - 1], a[i])].deep;
	}

	printf("%d\n", ans);

	return 0;
}

  

以上是关于倍增法求LCA——在线的主要内容,如果未能解决你的问题,请参考以下文章

倍增法求LCA

倍增法求lca:暗的连锁

lca(最近公共祖先(在线)) 倍增法详解

LCA(倍增在线算法) codevs 2370 小机房的树

LCA 模板

SPOJ QTREE2 (LCA - 倍增 在线)