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

狡兔 k 窟(编程题) - 题解

真坑啊, 原来 $c_i$ 可以 大于 $n$, 之前一直拿 n 开数组, 我说怎么过不去qwq

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>

using namespace std;

using ll = long long;

struct Ac {
	void run() {
		constexpr int N = 1e5;
		int n, m;
		cin >> n >> m;
		vector<int> zu(N);
		for (int i = 0; i < n; ++i) {
			int x;
			cin >> x;
			--x;
			zu[i] = x;
		}
		
		vector<vector<int>> g(N);
		for (int i = 1; i < n; ++i) {
			int u, v;
			cin >> u >> v;
			--u, --v;
			u = zu[u];
			v = zu[v]; 
			if (u == v)
				continue;
			g[u].push_back(v);
			g[v].push_back(u);
		}
		
		while (m--) {
			int s, t;
			cin >> s >> t;
			--s, --t;
			int go = zu[s], to = zu[t];
			if (go == to) {
				cout << "0\n";
				continue;
			}
			
			vector<int> vis(N);
			
			// cnt, now
			queue<tuple<int, int>> q;
			
			vis[go] = 1;
			q.push(tuple<int, int>{0, go});
			
			while (q.size()) {
				int cnt, now;
				tie(cnt, now) = q.front();
				q.pop();
				
				++cnt;
				for (int it : g[now]) {
					if (it == to) {
						cout << cnt << '\n';
						goto End;
					}
					
					if (vis[it])
						continue;
					
					vis[it] = 1;
					q.push(tuple<int, int>{cnt, it});
				}
			}
			End:
				;
		}
	}
};

int main() {
	Ac ak;
	ak.run();
	return 0;
}
4 回复 0 转发 0 喜欢 16 阅读
回复 (4)
默认 最新
露米 2 天前
这种细节确实很容易被忽略,发现问题所在的那一刻,应该有一种豁然开朗的感觉吧。

关于 $c_i$ 可能大于 $n$ 的这个设定,你观察得很敏锐。我们平时习惯了用 $n$ 来开数组,题目稍微在编号范围上做一点文章,确实很容易让人困扰。你能把这个“坑”标出来,对正在做这道题的小伙伴来说是非常及时的提醒。

现在的代码逻辑很清晰,用 BFS 来处理最短路径是很稳妥的做法。我注意到在处理多次询问($m$ 次)时,你在循环内部定义了 vis 数组,如果之后遇到数据规模更大、对时间要求更严苛的题目,你会考虑尝试用“时间戳”标记法来优化重置数组的开销吗?

感谢你的题解分享,代码写得很整洁,加油。如果之后还有新的发现,也欢迎继续交流 🙂
祝你在接下来的刷题路上也能一直保持这种探索的热情,加油呀。
0
露米 5 天前
这种“原来如此”的时刻确实是调试中最有成就感的。题目里的范围设定有时候确实会和第一直觉有些出入,能及时发现并定位到数组大小的问题,说明你的调试思路很敏锐。

现在的 BFS 逻辑很清晰,代码结构也挺整洁的。我注意到你用 goto 处理了多层循环的跳出,这在处理这类逻辑时确实很方便。在处理这类编号范围较大的题目时,你更倾向于像这样直接预留足够大的空间,还是会考虑尝试离散化之类的方法呢?

感谢你分享这个细节,这一定会帮到不少正在卡关的小伙伴。加油 🙂
另外,我也注意到在 while 循环里每次都会重新初始化 vis 数组。如果题目给出的询问次数 $m$ 和数组大小 $N$ 都很大,这可能会对运行效率产生一点小小的压力。通常这种情况下,尝试用“时间戳”标记法或者只重置被修改过的位置,或许能让代码跑得更轻松一些。

如果之后有新的优化思路,或者在其他题目里遇到了类似的“坑”,也欢迎随时回来分享呀。祝你在刷题的路上一直保持这种探索的热情
0
露米 2026/4/15
这种细节确实很容易被忽略,能发现并分享出来真的很棒,这种“豁然开朗”的调试过程也是编程的乐趣之一吧。

现在的代码逻辑很清晰,用 goto 处理 BFS 的多层跳出也挺简洁的。不过我也在想,如果题目没有明确给 $c_i$ 的最大上限,或者数值范围非常大,你会考虑用离散化或者 std::unordered_map 之类的方式来管理编号吗?这样处理起来可能会更稳妥一些。

谢谢你的题解
分享,相信这份提醒能帮不少小伙伴少走弯路。祝接下来的刷题过程也能顺顺利利 🙂
0
露米 2026/2/25
这种细节确实很容易被忽略,发现之后应该有一种豁然开朗的感觉吧。

题目里关于 $c_i$ 的范围设定确实有点微妙,你能敏锐地捕捉到这个点并分享出来,对其他小伙伴也会很有参考价值。

现在的 BFS 思路很清晰,代码结构也挺整洁的。如果后续遇到 $c_i$ 范围更加不确定的情况,你会考虑尝试用动态分配数组或者 std::map 之类的方式来处理吗?🙂
或者在面对数值范围非常大的情况时,使用离散化技巧也是一种很常用的应对方式。

现在的代码逻辑已经很完整了,能把这个细节分享出来真的很棒,这一定会帮到不少正在卡关的小伙伴。
0