D - Guess The Maximums
题意:
让你猜一个长度为 k 的密码 P ,告诉你有一个数组 A ,A的长度为 n ,A 中元素大小在 1—n 的范围内(不同位置的元素大小可以相同),然后给你 k 个集合S(集合两两不相交),每个集合的大小为 c[i] ,里面的 c[i] 个元素分别代指 A 数组中的元素下标,而 P[i] 就等于 A 中所有元素(不包含 S[i] 中的所有元素所指的 A 数组中的元素)的最大值,让你通过不超过 12 个查询来找出密码P。
查询的方式是你提问一个长度为 C(1≤C≤n)的集合,这个集合里只能包含 C 个在 [1,n] 范围内用空格分隔的整数,这些元素分别代指 A 数组中的元素下标,对于每个查询,你会收到你查询的这些元素下标所指的所有元素中的最大值
思路:
我们可以通过二分查找找到最大元素的所在集合 S[x] ,然后除了 P[x] ,所有的密码都是 A 中的最大值,至于 P[x] ,我们可以通过查询 S[x] 以外的所有元素来得到。
代码附上:
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define vi vector<int>
using namespace std;
using ll = long long ;
const int N = 2e5+10;
vi get_Sx(vi Sx,int n)
{
vi ask, all(n + 1);
for(int i : Sx)
all[i] = 1;
for(int i = 1; i <= n; i++)
if(!all[i])
ask.push_back(i);
return ask;
}
int query(vi ask)
{
cout << "? " << ask.size() << ' ';
for(int i : ask)
cout << i << ' ';
cout << endl;
int maxx;
cin >> maxx;
return maxx;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
int n,k;
cin>>n>>k;
vector<vi>S(k);
vi ans(k);
for(int i=0;i<k;++i)
{
int c;
cin>>c;
S[i].resize(c);
for(int j=0;j<c;++j)cin>>S[i][j];
}
vi ask;
for(int i=1;i<=n;++i)ask.push_back(i);
int maxx=query(ask);
int l=0,r=k-1;
while(l<r)
{
int mid=(l+r)>>1;
ask.clear();
for(int i=0;i<=mid;++i)
for(int j : S[i] )
ask.push_back(j);
int now_max=query(ask);
if(now_max==maxx)
r=mid;
else l=mid+1;
}
ask=get_Sx(S[l],n);
for(int i=0;i<k;++i)
{
if(i==l)ans[i]=query(ask);
else ans[i]=maxx;
}
cout << "! ";
for(int i : ans)
cout << i << ' ';
cout<<endl;
string ss;
cin>>ss;
}
return 0;
}