codeforces1370F1 / F2 The Hidden Pair (Hard Version)

https://codeforces.com/problemset/problem/1370/F2

先询问所有点,然后就可以得到一个u,v路径上的点,因为只有他们路径上的点的距离之和是最小的

然后以这个点rt建树,二分到rt的长度可以找到较远的那个端点v,然后通过距离相减,再询问一下除去v到rt的那个距离的点,就可以找到另一个端点

然而这个询问需要12次。。。

题解的一个小优化,最远距离最小是sumdis/2,最大是sumdis,所以区间减少了一半。。。。就可以减少一次二分。。。。

#include<bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
const int maxl=1e3+10;

int n,ans1,ans2,rt,sumdis;
vector <int> e[maxl];
int dep[maxl],fa[maxl];
vector<int> a[maxl],tmp;
typedef pair<int,int> p;
bool vis[maxl];
char s[10];

inline p qry()
{
	int len=tmp.size();
	printf("? %d",len);
	for(int i=0;i<len;i++)
		printf(" %d",tmp[i]);
	puts("");
	fflush(stdout);
	p ret;
	scanf("%d%d",&ret.fir,&ret.sec);
	return ret;
}

inline void dfs(int u)
{
	vis[u]=true;a[dep[u]].push_back(u);
	for(int v:e[u])
	{
		if(vis[v]) continue;
		dep[v]=dep[u]+1;
		fa[v]=u;dfs(v);
	}
}

inline void prework()
{
	scanf("%d",&n);
	for(int i=0;i<=n;i++)
		e[i].clear(),a[i].clear(),vis[i]=false;
	int u,v;
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d",&u,&v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	tmp.clear();
	for(int i=1;i<=n;i++)
		tmp.push_back(i);
	p ret=qry();
	rt=ret.fir;sumdis=ret.sec;
	dep[rt]=0;fa[rt]=0;
	dfs(rt);
}

inline void mainwork()
{
	int ans=0,l=sumdis/2,r=0,mid;
	p ret;
	for(int i=1;i<=n;i++)
		r=max(r,dep[i]);
	r=min(sumdis,r);
	while(l<=r)
	{
		mid=(l+r)>>1;
		tmp.clear();
		for(int v:a[mid])
			tmp.push_back(v);
		ret=qry();
		if(ret.sec==sumdis)
			l=mid+1,ans=mid,ans1=ret.fir;
		else
			r=mid-1;
	}
	int out=ans1;
	while(dep[out]>sumdis-ans)
		out=fa[out];
	tmp.clear();
	for(int v:a[sumdis-ans])
	if(v!=out)
		tmp.push_back(v);
	if(!tmp.empty())
	{
		ret=qry();
		ans2=ret.fir;
	}
	else
		ans2=rt;
}

inline void print()
{
	printf("! %d %d\n",ans1,ans2);
	fflush(stdout);
	scanf("%s",s);
}

int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/107222414