题目链接
https://codeforces.com/problemset/problem/1592/D
题目大意
一道交互题
给你一个生成树 每个节点之间的边的值 是两个节点值的gcd
你可以问最多12次
每次提出询问k个节点里最大的边值是多少
题目思路
我一开始想的是从点去考虑问题 类似于树上搜索这种 但是实际上这样是不好二分的
你无法准确的让两边的叶子节点值相等 这就会导致最终二分的次数大于十二次
而且以点判断还会有一种问题 就是当你二分完毕之后有可能这个最大边的两个节点分别落在那两部分中 就会导致永远无法查询到
所以转向去从边入手去思考问题
将n-1个边二分就能保证每次二分的两部分中一定有一部分包含那个最大边
但是也会出现一个问题: 如下图所示比如最大边是3 但是二分的红边区域和黑边区域都能包含这个3
因为交互返回给你的答案是包含你给出点的最大距离
因此我们加上一个dfs序保证这个边的顺序是连续的 这样就完美克服这个问题!
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
typedef long long ll;
int t,n,ans;
typedef pair<int ,int >PII;
PII p[maxn];
set<int >s;
vector<int >v[maxn];
int cnt=0;
int dfs(int now,int fa)
{
int x=now;
for(int i=0;i<v[x].size();i++)
{
int y=v[x][i];
if(y==fa)continue;
p[++cnt]=PII{x,y};
dfs(y,x);
}
}
int main()
{
cin>>n;
for(int i=1;i<=n-1;i++)
{
scanf("%d %d",&p[i].first,&p[i].second);
v[p[i].first].push_back(p[i].second);
v[p[i].second].push_back(p[i].first);
}
dfs(1,0);
//cout<<cnt<<endl;
cout<<"? "<<n;
for(int i=1;i<=n;i++)cout<<" "<<i;
cout<<endl;
cin>>ans;
int l=1,r=n-1;
int mid;
while(l<r)
{
mid=(l+r)/2;
if(l==r)
{
cout<<"! "<<p[l].first<<" "<<p[l].second<<endl;
break;
}
s.clear();
for(int i=l;i<=mid;i++)
{
s.insert(p[i].first);
s.insert(p[i].second);
}
cout<<"? "<<s.size();
set<int >::iterator it=s.begin();
for(;it!=s.end();it++)
{
cout<<" "<<*it;
}
cout<<endl;
cin>>t;
if(t==ans)
{
r=mid;
}
else l=mid+1;
}
cout<<"! "<<p[l].first<<" "<<p[l].second<<endl;
return 0;
}