题意:有一个树,大小n(2<=n<=2000),最多[n/2]次询问,得到这棵树的所有边。每次询问给定一个整数x,然后得到所有点到该点的最短距离。
题解:第一次消去点1(最开始顺便取个点然后分层),然后每次询问“叶子节点”,同时删去“无影响”(不能再连的点)的点,每次至少可以删去两个点,即叶子节点,与叶子节点相连的点(注意,如果与叶子节点相连的点的深度与查询的点x相等,那么也不能再连了)。(自己想一想吧)
总结:这种构造问题,应该有严格的规律!!!
代码:
#include <bits/stdc++.h>
// #define int long long
// #define ll long long
#define pb push_back
#define dbg(x) cout << #x << "===" << x << endl
#define ed(x, y) (x) * n + (y)
using namespace std;
const int N = 2e3 + 10;
const int mod = 1e9 + 7;
int n, a[N], b[2][N], cnt;
int vis[N], dep[N];
vector<int> g[N];
int mp[N * N];
int cx;
signed main() {
cin >> n;
int i, j, k;
int mx = 0, t, Mi, Mx;
//初始化
printf("? %d\n", 1);
fflush(stdout);
for (i = 1; i <= n; i++) {
scanf("%d", &dep[i]);
mx = max(mx, dep[i]);
g[dep[i]].pb(i);
}
vis[1] = 1;
for (auto j : g[1]) {
Mi = 1, Mx = j;
mp[ed(Mi, Mx)] = 1;
b[0][++cnt] = Mi;
b[1][cnt] = Mx;
}
queue<int> q;
for (i = mx; i >= 0; i--) {
for (auto j : g[i]) {
if (!vis[j]) q.push(j);
}
}
for (i = 0; i <= mx; i++) g[i].clear();
mx = 0;
//不断消
while (!q.empty()) {
if (cnt == n - 1) break;
int x = q.front();
q.pop();
if (vis[x]) continue;
printf("? %d\n", x);
fflush(stdout);
for (i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (vis[i]) continue;
mx = max(mx, a[i]);
g[a[i]].pb(i);
}
for (i = 0; i < min(2, mx); i++) {
t = g[i][0];
for (auto j : g[i + 1]) {
Mi = min(t, j);
Mx = max(t, j);
if (mp[ed(Mi, Mx)]) continue;
mp[ed(Mi, Mx)] = 1;
b[0][++cnt] = Mi;
b[1][cnt] = Mx;
if (i == 1 && dep[j] == dep[x]) vis[j] = 1;
}
vis[t] = 1;
}
for (i = 0; i <= mx; i++) g[i].clear();
mx = 0;
}
printf("!\n");
fflush(stdout);
for (i = 1; i <= cnt; i++) {
printf("%d %d\n", b[0][i], b[1][i]);
fflush(stdout);
}
return 0;
}