传送门
首先有个结论:以
w
w
w为根的
l
c
a
(
u
,
v
)
lca(u,v)
lca(u,v)是到
u
,
v
,
w
u,v,w
u,v,w三点距离和最小的点。结论显然正确,证明略。
我们考虑所有满足 a s k ( u , v , w ) ≠ u , v , w ask(u,v,w)\ne u,v,w ask(u,v,w)=u,v,w的三元组 ( u , v , w ) (u,v,w) (u,v,w),对于 d = a s k ( u , v , w ) d=ask(u,v,w) d=ask(u,v,w)而言,假设有 c n t [ d ] cnt[d] cnt[d]个这样不同的三元组。假设 d e p [ d ] = i , i > 1 dep[d]=i,i>1 dep[d]=i,i>1,注意到 c n t [ d ] cnt[d] cnt[d]就是 d d d的三个支路上的子树的大小的乘积,那么有 c n t [ d ] = ( 2 h − i − 1 ) 2 ( 2 h − 2 h − i + 1 + 1 ) ≈ ( 2 h − i ) 2 ( 2 h − 2 ⋅ 2 h − i ) = x 2 ( 2 h − 2 x ) cnt[d]=(2^{h-i}-1)^2(2^h-2^{h-i+1}+1)\approx (2^{h-i})^2(2^h-2\cdot2^{h-i})=x^2(2^h-2x) cnt[d]=(2h−i−1)2(2h−2h−i+1+1)≈(2h−i)2(2h−2⋅2h−i)=x2(2h−2x),显然当 x = 2 h − 2 x=2^{h-2} x=2h−2的时候取得最大值,也就是说根节点的两个儿子的 c n t cnt cnt是最大的,也就是说随机取三元组,对应的 a s k ( u , v , w ) ask(u,v,w) ask(u,v,w)是根节点儿子的可能性最大。
然后本题的做法就非常简单了,利用上述特性找到根节点两个儿子,然后再 O ( n ) O(n) O(n)遍历所有节点(除了根节点两个儿子本身),看它与根节点的两个儿子的询问值是否为它自己,如果是,说明它是根节点。
mt19937 rnd(time(0));
int random(int n){
return (rnd()%n+n)%n;
}
int qry(int u,int v,int w){
printf("? %d %d %d\n",u,v,w);
fflush(stdout);
scanf("%d",&u);
if(u==-1)exit(0);
return u;
}
int cnt[maxn],a[maxn];
bool cmp(int a,int b){
return cnt[a]>cnt[b];
}
int main(){
int h,n;
scanf("%d",&h);
n=(1<<h)-1;
FOR(i,1,n+1)a[i]=i;
FOR(i,0,420){
int u=random(n)+1,v=random(n)+1,w=random(n)+1;
if(u==v || u==w || v==w){
i--;
continue;
}
int as=qry(u,v,w);
if(as==u || as==v || as==w)continue;
cnt[as]++;
}
int id=0,id2=0;
sort(a+1,a+1+n,cmp);
id=a[1],id2=a[2];
FOR(i,1,n+1){
if(i==id || i==id2)continue;
if(qry(i,id,id2)==i){
printf("! %d\n",i);
fflush(stdout);
return 0;
}
}
printf("! 1\n");
fflush(stdout);
}