【CF1438F】Olha and Igor
题目
题目链接:https://codeforces.com/problemset/problem/1438/F
这是一道交互题
给定一个高度为\(h\)的完美的二叉树(恰好有\(2^h-1\)个节点)。
你可以进行以下询问至多\(n+420\)次。
- 选择三个互不相同的点\(u,v,w\),你需要保证\(1\leq u,v,w\leq n\)。
- 交互库会返回当以\(w\)为根时\(u\)与\(v\)的lca。
你需要向交互库回答根的编号。
\(3\leq h\leq 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\n",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\n",s1,s2,i);
fflush(stdout);
scanf("%d",&x);
if (x==i) return printf("! %d\n",i),0;
}
return 0;
}

浙公网安备 33010602011771号