zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    ASP.NET MVC学习笔记-----ActionInvoker
    quartz启动报错
    THUSC 2021 游记
    C++下随机数的生成
    友链
    memset一些技巧
    CodeForces Round #705 总结&题解
    php计算两坐标距离
    vue中使用keepAlive组件缓存遇到的坑
    vue 中 keepAlive
  • 原文地址:https://www.cnblogs.com/stoorz/p/14602080.html
Copyright © 2011-2022 走看看