倍增法求LCA——在线

 B站视频讲解

首先暴力做法:

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

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

有一个不那么暴力的做法——倍增法。

预处理复杂度:O(nlogn)

询问复杂度:O(Qlogn)    Q : 询问组数 

其(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
", ans);

	return 0;
}

例题:

1、商务旅行 √

2、小机房的树 √

3、水果姐逛水果街 ×

原文地址:https://www.cnblogs.com/ExileValley/p/7762516.html