題目鏈接
題目大意
讀了我一個世紀
給你一個長爲n的數組和k個有關數組下標的集合,每個集合告訴你這個集合的大小,以及每個集合所包含數組的下標。這k個集合都沒有交集,要你找出k個ans,ans就是除了這個集合外,所有數組的最大值。
你有最多12次詢問,每次詢問就是你可以查詢任意自己組合的集合的最大值。
n<=1000
題目思路
看這個數據範圍就知道是要二分,首先你會發現,這k個值,要麼都是max(a[i]),要麼有一個不是,首先用一次查詢找出max(a[i]),再用10次以內的查詢二分max(a[i])的下標,判斷每個集合是否有這個下標,如果沒有直接輸出-1,如果有的話,那麼就直接查詢這個集合的補集的最大值作爲這個答案即可。正好最多12次真是妙不可言
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
int t,n,k,subset[maxn][maxn],num[maxn],ma;
bool mp[maxn];//標記
void query(int l,int r){
printf("? %d ",r-l+1);
for(int i=l;i<=r;i++){
printf("%d ",i);
}
fflush(stdout);
}
int main(){
scanf("%d",&t);
while(t--){
memset(mp,0,sizeof(mp));
scanf("%d %d",&n,&k);
for(int i=1;i<=k;i++){
scanf("%d",&num[i]);
for(int j=1;j<=num[i];j++){
scanf("%d",&subset[i][j]);
}
}
query(1,n);
scanf("%d",&ma);//詢問最大值
int l=1,r=n,pos=-1,x;//找ma的位置
while(l<=r){
int mid=(l+r)/2;
query(l,mid);
cin>>x;
if(x==ma){//ma在[l,mid]裏面
pos=mid;
r=mid-1;
}else{
l=mid+1;
}
}
int spe=-1,spema;//找可能最大值不是ma的那個集合
for(int i=1;i<=k;i++){
for(int j=1;j<=num[i];j++){
if(subset[i][j]==pos){//查詢這個集合在[1,n]的補集
spe=i;
for(int k=1;k<=num[i];k++){
mp[subset[i][k]]=-1;
}
printf("? %d ",n-num[i]);
for(int k=1;k<=n;k++){
if(mp[k]==0){
printf("%d ",k);
}
}
fflush(stdout);
scanf("%d",&spema);
}
}
}
printf("! ");
for(int i=1;i<=k;i++){
if(i!=spe){
printf("%d ",ma);
}else{
printf("%d ",spema);
}
}
fflush(stdout);
char s[20];
scanf("%s",s);
}
return 0;
}