【CF1438F】Olha and Igor

题目

题目链接: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;
}
原文地址:https://www.cnblogs.com/stoorz/p/14602080.html