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;
}