题目链接
令1是根,我们可以一层一层的递推出去。容易知道询问a,b如果结果是c,那么c就是a,b路径上的中点。我们可以先让根1和其他n-1个点都询问一遍,如果返回值是1,那么这些点就是第二层,深度为2的点。我们发现一个c点会对应两层的深节点,比如一条链1 2 3 4,如果询问1 3和1 4返回都会是2,那么我们就让2和3 4分别连一条边,表示3和4是可能和2直接连边的。然后因为第二层我们已经推出来有哪些点了,再去遍历第二层与他们可能的点,然后就推出来第三层的点了。然后一直推下去,就结束了。
不会算用了多少次,反正能过。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int long long
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=2e5+10;
const int mod=1e9+7;
#define fi first
#define se secondint n;
vector<int> d[2000],v[2000];void solve(){cin>>n;set<pii> e;for(int i=1;i<=n;i++)d[i].clear(),v[i].clear();d[1].push_back(1);for(int i=2;i<=n;i++){cout<<"? "<<1<<' '<<i<<endl;int x;cin>>x;if(x==1){d[2].push_back(i);e.insert({1,i});}else{v[x].push_back(i);}}for(int i=2;i<=n;i++){for(auto &a:d[i]){for(auto &b:v[a]){cout<<"? "<<a<<' '<<b<<endl;int x;cin>>x;if(x==a){int t=a,tt=b;if(t>tt) swap(t,tt);e.insert({t,tt});d[i+1].push_back(b);}else{v[x].push_back(b);}}}}cout<<"! ";for(auto &x:e){cout<<x.first<<' '<<x.second<<' ';}cout<<endl;
}
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int t=1;cin>>t;while(t--){solve();}return 0;
}