题目链接:F - HonestOrUnkind
题目大意:有 \(n=A+B\) 个人,其中 \(A\) 个人诚实,\(B\) 个人不诚实,你可以向 \(x\) 询问 \(y\) 是否诚实,在 \(2n\) 次询问内求出每一个人是否诚实,无解输出 Impossible
,\(A,B\leq 2000\)。
题解:若 \(A\leq B\) 那么如果有一个大小为 \(A\) 的不诚实群体指认诚实群体不诚实,指认自己的群体诚实(即模仿诚实的群体),那么就不行了。
我们询问 ? x y
,若返回 Y
,那么显然两人都诚实或两人都不诚实,若返回 N
,那么两人至少一人不诚实。
因为 \(A>B\),所以我们发现 N
直接把两个人一起甩掉就好了,最后肯定至少留下一个诚实的。
我们可以维护一个栈,从 \(1\sim n\) 扫,每一次用栈顶人询问当前人,如果回答 N
就跳过这个人并把栈顶弹掉,否则压栈。
操作次数 \(2n\),时间复杂度 \(O(n)\)。
#include <cstdio>
const int Maxn=2000;
int n;
int A,B;
bool query(int x,int y){
x--,y--;
printf("? %d %d\n",x,y);
fflush(stdout);
char s[5];
scanf("%s",s);
return (*s)=='Y';
}
int ans[Maxn<<1|5];
int st[Maxn<<1|5],st_top;
int main(){
scanf("%d%d",&A,&B);
if(A<=B){
puts("Impossible");
return 0;
}
n=A+B;
for(int i=1;i<=n;i++){
if(st_top==0){
st[++st_top]=i;
continue;
}
if(query(st[st_top],i)){
st[++st_top]=i;
}
else{
st_top--;
}
}
int u=st[st_top];
for(int i=1;i<=n;i++){
if(u==i){
ans[i]=1;
continue;
}
if(ans[i]==0){
ans[i]=query(u,i);
}
}
printf("! ");
for(int i=1;i<=n;i++){
printf("%d",ans[i]);
}
puts("");
return 0;
}