【luogu CF1061F】Lost Root(思维)(树)

Lost Root

题目链接:luogu CF1061F

题目大意

给你一棵满 k 叉树,然后你每次可以询问一个点是否在两个点的路径中。
然后要你通过不超过 60n 次询问得出根节点的位置。

思路

这题其实挺神奇的。
首先我们可以算出树的深度 \(deg\)

我们考虑随机找,那找什么呢?
我们考虑找到两个叶子节点,然后要它们是在根的不同的儿子的子树中。

这样有什么用呢,那根节点就在它们的路径上,而且你可以知道这条路径上有多少个点:\(2deg-1\)

然后这个是有概率的:
选中同一个儿子的概率是 \(\frac{1}{k}\),反之则是 \(\frac{k-1}{k}\)
(然后这个东西最小的应该是 \(k=2\) 的时候是 \(\dfrac{1}{2}\)

然后叶子节点的数量占到了大概是 \(\frac{1}{2}\)
然后选到两个都是概率 \(\frac{1}{4}\)

所以整个选到的概率是 \(\frac{1}{8}\),然后你每次试要 \(n\) 次操作,所以你有接近 \(60\) 次尝试,这个找到的概率很大。

然后你找到之后就是要找到根节点。
那你找根节点也可以用同样类似的方法,你之前记录下在那两个点之间的点,然后依次找。
然后如果它在 \(deg-1\) 个点和其中一个叶子节点的路径之间的话,那它就是根节点。(显然)

然后就可以了。

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#define Pass fflush(stdout)

using namespace std;

int n, k, deg, sum;
int in[1501], x, y;

int get_deg(int n, int k) {
	int re = 1, now = 1, tot = 1;
	while (tot < n) {
		now *= k; re++; tot += now;
	}
	return re;
}

bool check(int x, int y) {
	int num = 0;
	for (int i = 1; i <= sum; i++)
		if (in[i] != y) {
			printf("? %d %d %d\n", x, in[i], y);
			Pass;
			char tmp; tmp = getchar(); while (tmp != 'N' && tmp != 'Y') tmp = getchar();
			if (tmp == 'N') getchar();
				else getchar(), getchar();
			if (tmp == 'Y') {
				num++;
			}
		}
	if (num == deg - 1) return 1;
	return 0;
}

int main() {
	srand(114514);
	
	scanf("%d %d", &n, &k);
	
	deg = get_deg(n, k); deg--;
	
	while (sum != 2 * deg - 1) {//直接随机找
		sum = 0;
		memset(in, 0, sizeof(in));
		x = rand() % n + 1; y = rand() % n + 1;
		while (x == y) y = rand() % n + 1;
		for (int i = 1; i <= n; i++)
			if (i != x && i != y) {
				printf("? %d %d %d\n", x, i, y);
				Pass;
				char tmp; tmp = getchar(); while (tmp != 'N' && tmp != 'Y') tmp = getchar();
				if (tmp == 'N') getchar();
					else getchar(), getchar();
				if (tmp == 'Y') {
					in[++sum] = i;
				}
			}
	}
	
	for (int i = 1; i <= sum; i++) {//然后直接找到根
		if (check(x, in[i])) {
			printf("! %d", in[i]);
			return 0;
		}
	}
	
	return 0;
}
posted @ 2021-12-12 02:00  あおいSakura  阅读(28)  评论(0编辑  收藏  举报