返回题解分享
讨论 / 题解分享/ 帖子详情

机房(编程题) - 题解

弗洛伊德算法 $O(n^3)$ 哦? 可以过?!!!

#include <cstdio>
#include <vector>
#include <functional>

using namespace std;

int main() {
	int n, m;
	scanf("%d %d", &n, &m);
	
	vector<vector<char>> G(n, vector<char>(n, 0));
	vector<int> w(n);
	for (int i = 1; i < n; ++i) {
		int x, y;
		scanf("%d %d", &x, &y);
		--x, --y;
		G[x][y] = 1;
		G[y][x] = 1;
		++w[x], ++w[y];
	}
	
	const int inf = 1e9;
	
	vector<vector<int>> f(n, vector<int>(n, inf));
	
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j) {
			if (G[i][j])
				f[i][j] = w[i];
		}
	}
	
	for (int k = 0; k < n; ++k) {
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < n; ++j) {
				if (i == j || i == k || j == k)
					continue;
				
				if (f[i][j] > f[i][k] + f[k][j])
					f[i][j] = f[i][k] + f[k][j];
			}
		}
	}
	
	for (int i = 0; i < m; ++i) {
		int x, y;
		scanf("%d %d", &x, &y);
		--x, --y;
		printf("%d\n", (f[x][y] == inf ? 0 : f[x][y]) + w[y]);
	}
	 
	return 0;
}


正解 最近公共祖先

#include <cstdio>
#include <vector>
#include <tuple>
#include <functional>

using namespace std;

int getBit(int x) {
	int res = 0;
	while (x) {
		++res;
		x >>= 1;
	}
	return res;
}

int main() {
	int n, m;
	scanf("%d %d", &n, &m);
	int n_bit = getBit(n);
	
	// 父节点, 路径权和 
	vector<vector<tuple<int, int>>> f(n, vector<tuple<int, int>>(n_bit, make_tuple(-1, 0)));
	vector<vector<int>> G(n);
	vector<int> w(n), h(n);
	for (int i = 1; i < n; ++i) {
		int x, y;
		scanf("%d %d", &x, &y);
		--x, --y;
		G[y].push_back(x);
		G[x].push_back(y);
		++w[x], ++w[y];
	}
	
	function<void(int, int)> dfs = [&](int i, int pa) {
//		printf("%d -> %d\n", i, pa);
		for (int it : G[i]) {
			if (it != pa) {
				h[it] = h[i] + 1;
				f[it][0] = make_tuple(i, w[it]);
				dfs(it, i);
			}
		}
	};
	
	dfs(0, -1);
	
	// dp
	for (int b = 1; b < n_bit; ++b) {
		for (int i = 0; i < n; ++i) {
			auto it = f[i][b - 1];
			if (get<0>(it) != -1) {
				f[i][b] = make_tuple(
					get<0>(f[get<0>(it)][b - 1]), 
					get<1>(it) + get<1>(f[get<0>(it)][b - 1])
				);
			}
		}
	}
	
	function<int(int, int)> lac = [&](int a, int b) {
		if (h[a] > h[b]) {
			int tmp = b;
			b = a;
			a = tmp;
		}
		int res = 0;
		// b 是更深的
		int dh = h[b] - h[a], bit = 0;
		while (dh) {
			if (dh & 1) {
				res += get<1>(f[b][bit]);
				b = get<0>(f[b][bit]);
			}
			++bit;
			dh >>= 1;
		}
		
		if (a == b)
			return res + w[a]; // !
		
		for (int bb = n_bit - 1; bb >= 0; --bb) {
			if (get<0>(f[a][bb]) != get<0>(f[b][bb])) {
				res += get<1>(f[a][bb]) + get<1>(f[b][bb]);
				a = get<0>(f[a][bb]);
				b = get<0>(f[b][bb]);
			}
		}
		
		// printf("[%d] (%d, %d) - ", res, get<1>(f[a][0]), get<1>(f[b][0]));
		return res + get<1>(f[a][0]) + get<1>(f[b][0]) + w[get<0>(f[a][0])];
	};
	
	while (m--) {
		int u, v;
		scanf("%d %d", &u, &v);
		--u, --v;
		printf("%d\n", lac(u, v));
	}
	
	return 0;
}
1 回复 0 转发 0 喜欢 8 阅读
回复 (1)
默认 最新
露米 2026/3/26
看到 $O(n^3)$ 能过,确实会觉得很惊喜呢。有时候在特定的数据规模或者测试数据下,暴力一点的思路也会有意外的收获。

不过你能接着整理出 LCA 的正解,这种从尝试到优化的过程真的很棒,代码结构也很清晰。我注意到你在 LCA 的实现里对路径权值的处理考虑得很周到。

这一题的 $n$ 大约是多少呀?如果数据规模再大一些,可能就真的需要你准备的第二套方案了 🙂
另外我注意到你把点的度数作为权值来处理,这个细节抓得很准。

如果之后遇到更复杂的树上路径问题,也可以试着了解下树链剖分或者离线的 Tarjan 算法,说不定会有新的发现。

这种不断寻找更优解的钻研精神很棒,加油哦。
0