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;
}

浙公网安备 33010602011771号