CF 1370 F2 - The Hidden Pair (Hard Version) 交互 树上二分

41 篇文章 1 订阅
3 篇文章 0 订阅

对交互的敏感度还是不够高。。

虽然知道是二分,但比赛时想了半天没思路。

正解:

显然:第一次询问所有点,会告诉你路径长度d,和路径上的一个点。

然后把这个点设为rt,做为树的根节点。然后查询dep[i]==x 的所有节点。

如果给出的d大于路径长度,说明当前深度没有路径上的点,(即隐藏的两点a,b距离rt的距离都小于等于x)

然后就可以二分了,二分的下界设为1,上界设为d。

log(1000)=10,

询问得出其中一个点a,再询问距离a ,长度d的所有节点,可一次求得b。

总共12次询问。

我们可以通过把二分的下届设为(d+1)/2.  来少一次询问。

因为不可能两个点a,b距离rt的距离都小于2/d.(比较显然)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int M = 1e3+7;

int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}

int a[M],sz;
int n,m;
int dep[M];
int rt,d,tid;
pii ask()
{
	cout<<"?";
	cout<<" "<<sz;
	for(int i=1;i<=sz;i++)cout<<" "<<a[i];
	cout<<endl;
	int x,y;
	cin>>x>>y;
	return {x,y};
}
void dfs(int x,int fa)
{
	for(int i=head[x];i;i=ee[i].nxt)
	{
		int y=ee[i].to;
		if(y==fa)continue;
		dep[y]=dep[x]+1;
		dfs(y,x);
	}
}
bool ck(int x)//在深度x的点中,有无在隐藏点路径上的点 
{
	sz=0;for(int i=1;i<=n;i++)if(dep[i]==x)a[++sz]=i;
	if(sz==0)return false;
	pii tp=ask();
	if(tp.second!=d)return false;
	tid=tp.first;
	return true;
}
void gao(int x,int fa,int p)
{
	if(p==d){
		a[++sz]=x;
		return ;
	}
	for(int i=head[x];i;i=ee[i].nxt)
	{
		int y=ee[i].to;
		if(y==fa)continue;
		gao(y,x,p+1);
	}
}

int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		init();
		for(int i=1;i<n;i++){
			int u,v;
			cin>>u>>v;
			add(u,v,1),add(v,u,1);
		}
		sz=0;for(int i=1;i<=n;i++)a[++sz]=i;
		pii tp = ask();
		rt=tp.first,d=tp.second;
		dep[rt]=0;dfs(rt,0);
		int l=(d+1)/2,r=d,id=rt;
		while(l<=r)
		{
		//	cout<<rt<<" "<<dep[1]<<" "<<dep[2]<<" "<<dep[3]<<" ==   "<<l<< " -  "<<r<<endl;
			int mid=(l+r)/2;
			if(ck(mid))id=tid,l=mid+1;//仍然能找到在路径上的点 
			else r=mid-1;
		}
		sz=0;gao(id,0,0);
		tp = ask();
		cout<<"! "<<id<<" "<<tp.first<<endl;
		char s[111];
		cin>>s;
	}
	return 0;
}
/*
4

2 3
3 1
3 4

4 2
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值