2023 ICPC ECfinal J

J. Travel 2

思维,模拟搜索。

如果从 \(u\) 选一条边到 \(v\),然后再从 \(v\) 又刚好选到一条边回来 \(u\),那么 \(u-v\) 这条边我们已经知道它分别在 \(u\)\(v\) 里的排名了,一共有 \(m\) 条边,显然 \(2m\) 次可以拿来确定有哪些边。

一开始什么都不知道,考虑从 \(1\) 的第 \(1\) 条边一直出发,以此到 \(v_1,v_2,...\) 的第一条边去找,假如某次询问后又回到了上一个点,那么就从上一个点的第二条边出发,以此往复。

假如某次询问后回到 \(u\) 这个点,而这个点已经已经确定了所有的边,那么可以把这个点标记,接下来去从它所连向的点中找没有确定完所有边的点 \(v\) 去进行上面的搜索过程,确定完 \(v\) 的所有边后再从 \(v\) 回溯到 \(u\),再去找下一个点。

手动模拟一下可以发现的是,除去那 \(2m\) 条确定边的过程中,已经确定完所有边的 \(u\) 去找未确定完所有边的 \(v\) 的过程,就是以 \(u\)\(v\) 的父节点的图中的一颗生成树的树边,而这样的查询过程,向下只会找 \(n-1\) 次,向上回溯需要 \(n-1\) 次,总次数 \(2m+2(n-1)\) 次。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {

	const int N = 2500;

	std::vector<int> d(N);
	auto move = [&](int i)->int{
		std::cout << "> " << i << std::endl;
		int x;
		std::cin >> x >> d[--x];
		return x;
	};

	int x, n = 0;
	std::cin >> x >> d[--x];

	std::vector<char> vis(N);
	std::vector<std::vector<int>> adj(N);
	auto dfs = [&](auto &&self, int u)->void{
		n = std::max(n, u);

		if(adj[u].size() < d[u]) {
			int v = move(adj[u].size() + 1);
			adj[u].push_back(v);
			self(self, v);
		} else{
			vis[u] = 1;
			for(int i = 0; i < adj[u].size(); i += 1) {
				int v = adj[u][i];
				if(vis[v]) continue;

				move(i + 1);
				self(self, v);
				for(int j = 0; j < adj[v].size(); j += 1) {
					if(adj[v][j] == u) {
						move(j + 1);
						break;
					}
				}
			}
		}
	};
	dfs(dfs, 0);

	std::cout << "! ";
	for(int i = 0; i < n; i += 1) {
		for(auto &v : adj[i]) {
			if(i > v) {
				continue;
			}
			std::cout << i + 1 << " " << v + 1 << " ";
		}
	}
	std::cout << std::endl;

	std::string s;
	std::cin >> s;

}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t;
	std::cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}
posted @ 2025-10-12 21:54  Ke_scholar  阅读(7)  评论(0)    收藏  举报