题目
题目链接:https://codeforces.com/problemset/problem/1438/F
这是一道交互题
给定一个高度为(h)的完美的二叉树(恰好有(2^h-1)个节点)。
你可以进行以下询问至多(n+420)次。
- 选择三个互不相同的点(u,v,w),你需要保证(1leq u,v,wleq n)。
- 交互库会返回当以(w)为根时(u)与(v)的lca。
你需要向交互库回答根的编号。
(3leq hleq 18)。
思路
这种题这辈子也做不出来的。
考虑询问 ((x,y,z)) 的本质,其实就是返回树上距离 (x,y,z) 三点距离之和最小的点。显然有且仅有一个这样的点。
对于一个子树大小为 (s) 的点(完全二叉树左右子树大小一样),满足该点到 (x,y,z) 三个点距离最小的方案数为 (s^2(n-2s-1))。其中 (n) 是树大小,也就是 (2^h-1)。
不难发现这个点是根节点两个儿子时,方案数最多。
那么考虑随机 (420) 次点对,然后统计每一个点被返回的次数,有极大概率出现次数最多的两个点就是根的儿子。CF 上题解也没有证明 (420) 次随机后正确的期望。所以就假装对的吧。
最后枚举所有点,与得到出现次数最多的两个点询问一遍,判断是不是根即可。
时间复杂度 (O(2^h))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=(1<<18)+10;
int n,x,cnt[N],s1,s2;
int random(int l=1,int r=1e9)
{
return ((rand()<<15)|rand())%(r-l+1)+l;
}
int main()
{
srand(1023);
scanf("%d",&n);
n=(1<<n)-1;
for (int i=1;i<=420;i++)
{
int a=random(1,n),b=random(1,n),c=random(1,n);
while (a==b || b==c || a==c) a=random(1,n),b=random(1,n),c=random(1,n);
printf("? %d %d %d
",a,b,c);
fflush(stdout);
scanf("%d",&x);
cnt[x]++;
}
for (int i=1;i<=n;i++)
if (cnt[i]>cnt[s1]) s2=s1,s1=i; else
if (cnt[i]>cnt[s2]) s2=i;
for (int i=1;i<=n;i++)
{
if (i==s1 || i==s2) continue;
printf("? %d %d %d
",s1,s2,i);
fflush(stdout);
scanf("%d",&x);
if (x==i) return printf("! %d
",i),0;
}
return 0;
}