【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;
}
posted @ 2021-03-31 16:18  stoorz  阅读(78)  评论(0)    收藏  举报