A
ImmatureDreamer · · 题解
首先通过二分询问
接下来,考虑每次排除一些
:::align{center} :::
对每次询问,若最大的连通块在其父亲方向,则至少排除
这样,就在至多
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e4 + 5;
int n, dep[N], root, tot_size, siz[N], xd, mn, p;
bool vis[N];
vector<int> G[N];
int ask(int u, int k){
cout << "? " << u << " " << k << endl;
int x;
cin >> x;
return x;
}
void dfs(int u){
siz[u] = 1;
for(int v : G[u])
if(!vis[v]){
dfs(v);
siz[u] += siz[v];
}
if(dep[u] <= xd && max(siz[u], tot_size - siz[u]) < mn)
mn = max(siz[u], tot_size - siz[u]), p = u;
}
void solve(){
cin >> n;
root = 1, tot_size = n;
for(int i = 1; i <= n; ++ i)
vis[i] = 0, G[i].clear();
int max_dep = 0;
for(int i = 2; i <= n; ++ i){
int f;
cin >> f;
G[f].push_back(i);
dep[i] = dep[f] + 1;
max_dep = max(max_dep, dep[i]);
}
int l = 0, r = max_dep;
while(l <= r){
int mid = (l + r) >> 1;
if(ask(1, mid)) r = mid - 1, xd = mid;
else l = mid + 1;
}
while(1){
mn = n + 1;
dfs(root);
if(ask(p, xd - dep[p])){
root = p;
tot_size = siz[p];
if(dep[p] == xd)
break;
}
else
vis[p] = 1;
}
cout << "! " << p << endl;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t;
cin >> t;
while(t --)
solve();
return 0;
}