CF1617D2 Too Many Impostors - 交互
题意
有 \(n\) 个人(保证 \(n\bmod 3=0\)),其中有 \(k\) 个 impostor(保证 \(n< 3k< 2n\))和 \(n-k\) 个 crewmate,但你不知道 \(k\) 具体是多少。你可以询问至多 \(n+6\) 次,每次给出 \(a,b,c(1\le a,b,c\le n)\),你可以得知 \(a,b,c\) 中是 impostor 多还是 crewmate 多。
找到所有的 impostors。
题解
找到任意一个 impostor 和一个 crewmate
一个结论是,对于四个人 \(x,y,z,w\),若询问 \((x,y,z)\) 和 \((y,z,w)\) 得到的结果不同,那么我们可以确定 \(x\) 和 \(w\) 的身份。
将这 \(n\) 个人分成 \(\frac{n}{3}\) 组,第 \(i\) 组是 \((3i-2,3i-1,3i)\)。若某一组中 impostor 更多,则称这一组是 \(1\);否则这一组是 \(0\)。
用 \(\frac{n}{3}\) 次询问得到每组是 \(0\) 还是 \(1\)。由于 \(\frac{n}{3}<k<\frac{2n}{3}\),所以一定存在一组 \(p\) 满足它是 \(0\);也一定存在一组 \(q\) 满足它是 \(1\)。
我们询问 \((3p-1,3p,3q-2),(3p,3q-2,3q-1)\)。我们知道,在 \(3p-2,3p-1,3p,3q-2,3q-1,3q\) 中,一定有相邻的四个人 \(x,y,z,w\) 满足 \((x,y,z)\) 和 \((y,z,w)\) 询问的结果不同。根据上面的结论,我们可以在这六个人里找到一个 impostor 和一个 crewmate,使用了 \(\frac{n}{3}+2\) 次操作。
确定所有人的身份
找到了一个 impostor 和一个 crewmate,设它们分别是 \(a\) 和 \(b\)。
对于第 \(i\) 组,我们已经知道了它是 \(0\) 还是 \(1\)。不妨设它是 \(0\)。我们询问 \((3i-2,3i-1,b)\),分类讨论这个询问的结果:
- 说明 \(3i-2\) 和 \(3i-1\) 都是 impostor。再询问 \((3i,a,b)\) 就可以得到 \(3i\) 的身份。
- 说明 \(3i\) 一定是 impostor,且 \(3i-2\) 和 \(3i-1\) 中恰好有一个 impostor。询问 \((3i-2,a,b)\) 就可以得到 \(3i-2\) 和 \(3i-1\) 的身份。
若这一组是 \(1\),类似地分类讨论即可。
总共使用了 \(n+2\) 次操作。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
const int N=1e4+5;
int Query(int a,int b,int c){
cout<<"? "<<a<<' '<<b<<' '<<c<<endl;
int res;cin>>res;
return res;
}
int T,n,res[N];
int main(){
cin>>T;
while(T--){
cin>>n;
int _imp=0,_crm=0;
For(i,1,n/3){
res[i]=Query(i*3-2,i*3-1,i*3);
if(res[i]) _crm=i;
else _imp=i;
}
int res1=Query(_imp*3-1,_imp*3,_crm*3-2),res2=Query(_imp*3,_crm*3-2,_crm*3-1),imp,crm;
if(res[_imp]!=res1) imp=_imp*3-2,crm=_crm*3-2;
else if(res1!=res2){
if(res1) imp=_crm*3-1,crm=_imp*3-1;
else imp=_imp*3-1,crm=_crm*3-1;
}else imp=_imp*3,crm=_crm*3;
vector<int> ans;
ans.push_back(imp);
For(i,1,n/3){
if(_imp==i||_crm==i){
For(j,i*3-2,i*3) if(j!=imp&&j!=crm&&Query(j,imp,crm)==0) ans.push_back(j);
continue;
}
if(res[i]){
int x=Query(i*3-2,i*3-1,imp);
if(x){if(Query(i*3,imp,crm)==0) ans.push_back(i*3);}
else ans.push_back(Query(i*3-2,imp,crm)?i*3-1:i*3-2);
}else{
int x=Query(i*3-2,i*3-1,crm);
if(!x){ans.push_back(i*3-2),ans.push_back(i*3-1);if(Query(i*3,imp,crm)==0) ans.push_back(i*3);}
else ans.push_back(i*3),ans.push_back(Query(i*3-2,imp,crm)?i*3-1:i*3-2);
}
}
sort(ans.begin(),ans.end());
cout<<"! "<<ans.size()<<' ';
for(int x:ans) cout<<x<<' ';
cout<<endl;
}
return 0;
}