题目:click
题意:题目是真滴长。。。,简而言之,将A数组中挑选出k个两两取交集都是空集的集合,k位的密码,对应k个集合,是除自己集合以外的数字的最大值就是该位的密码。
注意题目中说了最多12次询问,n的范围是1000,可知log(n)一定可以,并且还多余了两次询问机会,第一次去找A数组中1-n的最大值记录下来,之后我们取二分询问出最大值在A数组的下标,最后一次询问就是最大值下标所在此集合,问除此集合里面的数字其余下标在A数组中的最大值,注意一点可能最大值没有挑选进入 集合中,密码则全是最大值。
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
int a[1010][1010];
int siz[1010],ans[1010];
int query(int x)
{
printf("?");
printf(" %d",x);
for(int i=1;i<=x;i++)
{
printf(" %d",i);
}
printf("\n");
cout.flush();
int ans;
scanf("%d",&ans);
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k,i,j;
scanf("%d %d",&n,&k);
for(i=1;i<=k;i++)
{
scanf("%d",&siz[i]);
for(j=1;j<=siz[i];j++)
{
scanf("%d",&a[i][j]);
}
}
printf("? %d",n);
for(i=1;i<=n;i++)
{
printf(" %d",i);
}
printf("\n");
cout.flush();
int MAX;
scanf("%d",&MAX);
int l=1,r=n;
int res=1;
while(l<=r)
{
int mid=(l+r)>>1;
if(query(mid)==MAX)
{
r=mid-1;
res=mid;
}
else
l=mid+1;
}
int temp=-1;
memset(ans,0,sizeof(ans));
for(i=1;i<=k;i++)
{
bool flag=false;
for(j=1;j<=siz[i];j++)
{
if(a[i][j]==res)
{
flag=true;
temp=i;
}
}
if(!flag)
{
ans[i]=MAX;
}
}
if(temp!=-1)
{
map<int,int>hh;
for(j=1;j<=siz[temp];j++)
{
hh[a[temp][j]]=1;
}
printf("? ");
printf("%d ",n-siz[temp]);
for(i=1;i<=n;i++)
{
if(hh[i])
continue;
else
{
printf("%d ",i);
}
}
printf("\n");
cout.flush();
scanf("%d",&ans[temp]);
}
printf("! ");
for(i=1;i<=k;i++)
{
printf("%d ",ans[i]);
}
printf("\n");
cout.flush();
string anss;
scanf("%s",anss.c_str());
}
return 0;
}