LCA的学习

LCA入门:
LCA(x, y):x和y的最近公共祖先
入门题目:hdu_2586
解题思路:
首先我们通过一个bfs得到所有点的深度以及所有点到1(树根)的距离;通过在bfs的过程中我们通过一个dp去的更行每一个节点的 f 数组;
然后我们通过lca直接得到查询中2个点的lca;
最后用 dist[x] + dist[y] - 2 * dist[ lca(x, y) ],得到最终的答案。

f数组的含义:f[i][j]:表示 i 这个节点的 第 2 ^ j 祖先;所以可以得到他的一个状态转移方程:
f[ i, j ] = f[ f[ i, j - 1 ], j - 1 ] 。

lca算法思路:
首先对于x和y,我们需要他们的深度;不妨设d[x] < d[y];那么我们我们首选需要做的是找到y节点的祖先设其祖先为fy, 那么我们需要找到的是d[fy] == f[x],的这个节点。我们是一定可以找到的;寻找的方法:通过f数组去寻找;然后直接让 y = fy;
然后,我们接下来首先判断此时的 x == y?,如果等于那x就是x 和 y 的最近公共祖先。否则的话我们进行后续的操作;
那么现在,我们就需要找到 x 和 y的lca,同样我们通过f数组去寻找,我们通过一个循环从最开始的f[x][i]开始找,此时f[x][i] 是一定等于 f[y][i]的(此时i = t);不断第让i自减,当f[x, i] != f[y, i]的时候就让 x = f[x, i], y = f[y, i],然后继续这个循环,最终我们一定会得到一个x,此时f[x, 0]就是x和y的lca.

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <queue>

using namespace std;

const int N = 4e4 + 5, M = 1e5 + 5;

int T, n, m, t;
int h[N], e[M], w[M], ne[M], idx;
int f[N][20], d[N], dist[N];
queue<int> q;

inline void init(void) {
	idx = 0;
	memset(h, -1, sizeof h);
	memset(d, 0, sizeof d);
}

inline void add(int a, int b, int c) {
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
	e[idx] = a, w[idx] = c, ne[idx] = h[b], h[b] = idx ++;
}

inline void bfs(void) {
	d[1] = 1; q.push(1);
	
	while(q.size()) {
		int u = q.front(); q.pop();
		for(int i = h[u]; i + 1; i = ne[i]) {
			int v = e[i];
			if(d[v]) continue;
			d[v] = d[u] + 1;
			dist[v] = dist[u] + w[i];
			f[v][0] = u;
			for(int j = 1; j <= t; j ++)
				f[v][j] = f[f[v][j - 1]][j - 1];
			q.push(v);
		}
	}
}

inline int lca(int x, int y) {
	if(d[x] > d[y]) swap(x, y);
	for(int i = t; i >= 0; i --)
		if(d[f[y][i]] >= d[x]) y = f[y][i];
	if(x == y) return x;
	for(int i = t; i >= 0; i --)
		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	return f[x][0];
}

int main(void) {
//	freopen("in.txt", "r", stdin);
	scanf("%d", &T);
	while(T --) {
		scanf("%d%d", &n, &m);
		init();
		t = (int)(log(n) / log(2)) + 1;
		for(int i = 1; i <= n - 1; i ++) {
			int a, b, c; 
			scanf("%d%d%d", &a, &b, &c);
			add(a, b, c);
		}
		
		bfs();
		
		for(int i = 1; i <= m; i ++) {
			int x, y; 
			scanf("%d%d", &x, &y);
			printf("%d\n", dist[x] + dist[y] - 2 * dist[lca(x, y)]);
		}
	}

	return 0;
} 
发布了136 篇原创文章 · 获赞 0 · 访问量 2986

猜你喜欢

转载自blog.csdn.net/weixin_42596275/article/details/102635852
lca
今日推荐