ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

#24 CF1438F

2022-06-01 22:03:03  阅读:152  来源: 互联网

标签:24 左子 CF1438F 一个点 int 右子 420 include


Olha and Igor

题目描述

点此看题

解法

自己想了一个 \(O(n^2)\) 的做法,好像也要基于 \(\tt lca\) 的出现频率这东西(多少沾点边了

考虑询问 \((u,v,w)\) 的另一种意义:在树上找到点 \(x\),使得 \(d(u,x)+d(v,x)+d(w,x)\) 最小。

发现如果我们随机三个不同的点问一次 \((u,v,w)\),考虑分析得到的结果:

  • 不可能是叶子。
  • 如果返回的 \(x\) 是真正的根,必须要其中一个点恰好是根,另外两个点分居左右子树。
  • 如果其中两个点在左子树,另外一个点在右子树,一定直接得到根的左儿子。
  • 如果其中两个点在右子树,另外一个点在左子树,一定直接得到根的右儿子。
  • 根据数感,出现其他的概率远小于根的左右儿子情况(概率可以计算出来,但是我懒

那么我们可以随机 \(420\) 次,认为出现频率最大的两个点就是根的左右儿子。设得到了 \(x,y\),那么我们枚举根,如果询问 \((x,y,i)\) 得到 \(i\),那么 \(i\) 就是真正的根,发现最坏情况下询问次数 \(n+420\)

#include <cstdio>
#include <random>
#include <algorithm>
using namespace std;
const int M = 1000005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,h,a[M],p[M];
int ask(int u,int v,int w)
{
	printf("? %d %d %d\n",u,v,w);
	fflush(stdout);
	return read();
}
signed main()
{
	h=read();n=(1<<h)-1;
	mt19937 z(114514);
	for(int i=0;i<420;i++)
	{
		int u=z()%n+1,v=z()%n+1,w=z()%n+1;
		while(u==v) v=z()%n+1;
		while(u==w || v==w) w=z()%n+1;
		a[ask(u,v,w)]++;
	}
	for(int i=1;i<=n;i++) p[i]=i;
	sort(p+1,p+1+n,[&](int i,int j)
	{return a[i]>a[j];});
	int x=p[1],y=p[2];
	for(int i=1;i<=n;i++)
		if(i!=x && i!=y && ask(x,y,i)==i)
		{
			printf("! %d\n",i);
			fflush(stdout);
			return 0;
		}
}

标签:24,左子,CF1438F,一个点,int,右子,420,include
来源: https://www.cnblogs.com/C202044zxy/p/16335876.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有