• 【CF1061F】Lost Root


    题目

    题目链接:https://codeforces.com/problemset/problem/1061/F
    有一棵节点编号未知的满 \(k\) 叉树,其上有 \(n\) 个节点,你每次可以询问一个点是否在另两个点的路径上。需要在 \(60\times n\) 次询问操作中找到这棵树的根。
    \(n\leq 1500\)\(2\leq k<n\)

    思路

    首先我们可以在 \(n\) 次询问内确定一个点 \(x\) 是不是叶子节点。只需要找另一个点 \(y\),然后对于所有点 \(z(z\neq x,z\neq y)\),如果均满足 ? y z x 回答是 NO,那么 \(x\) 就是叶子节点。
    然后由于这棵树是一个满 \(k(k>1)\) 叉树,所以它至少有 \(\frac{n}{2}\) 个叶子节点。也就是说如果不断随机点并判断是不是叶子,期望 \(2\) 次就可以找到一个叶子。
    然后再找到与 \(x\) 不在根节点同一子树内的另一个叶子节点 \(y\),依然随机,然后只需要 \(n\) 次询问判断 \(x\)\(y\) 的路径上节点数量是不是恰好为 \(2h-1\) 即可。其中 \(h\) 是树高,可以算出来。
    最后只需要找到 \(x\)\(y\) 之间的所有节点并按顺序排列,最后第 \(h\) 个就是根。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1510;
    int n,m,h,sum,mul,a[N];
    char s[10];
    
    bool check(int x)
    {
    	int y=(x==1)?2:1;
    	for (int i=1;i<=n;i++)
    		if (i!=x && i!=y)
    		{
    			printf("? %d %d %d\n",y,x,i);
    			fflush(stdout);
    			scanf("%s",s);
    			if (s[0]=='Y') return 0;
    		}
    	return 1;
    }
    
    bool check2(int x)
    {
    	int cnt=2;
    	for (int i=1;i<=n;i++)
    		if (i!=x && i!=a[1])
    		{
    			printf("? %d %d %d\n",x,i,a[1]);
    			fflush(stdout);
    			scanf("%s",s);
    			if (s[0]=='Y') cnt++;
    		}
    	if (cnt==2*h-1) return 1;
    	return 0;
    }
    
    int main()
    {
    	srand(time(0));
    	scanf("%d%d",&n,&m);
    	mul=1;
    	for (int i=1;i<=n;i++)
    	{
    		sum+=mul; mul*=m;
    		if (sum==n) { h=i; break; }
    	}
    	a[1]=rand()%n+1;
    	while (!check(a[1])) a[1]=rand()%n+1;
    	a[2]=rand()%n+1;
    	while (a[2]==a[1] || !check(a[2]) || !check2(a[2])) a[2]=rand()%n+1;
    	m=2;
    	for (int i=1;i<=n;i++)
    		if (i!=a[1] && i!=a[2])
    			for (int j=1;j<m;j++)
    			{
    				printf("? %d %d %d\n",a[j],i,a[j+1]);
    				fflush(stdout);
    				scanf("%s",s);
    				if (s[0]=='Y')
    				{
    					for (int k=m;k>j;k--) a[k+1]=a[k];
    					a[j+1]=i; m++; break;
    				}
    			}
    	printf("! %d\n",a[(m+1)/2]);
    	return 0;
    }
    
  • 相关阅读:
    MVP模式
    开源代码SlidingMenu的使用
    常用命令(Linux、Android、adb)
    一文搞清楚Minor GC、Major GC 、Full GC 之间的关系
    阿里最新38道Java面试题解析(MyBatis+消息队列+Redis)
    从5个方面让你真正了解Java内存模型
    深入理解JVM:元空间大小详细解析
    面试必问:JVM类加载机制详细解析
    5个点彻底搞清楚SpringBoot注解
    8种创建Java线程的方式,你知道几个?
  • 原文地址:https://www.cnblogs.com/stoorz/p/15682397.html
Copyright © 2020-2023  润新知