Link.

ATcoder
Luogu

Description.

交互题。
有很多好人和坏蛋,你知道有 \(a\) 个好人和 \(b\) 个坏蛋。
你每次可以问 \(x\)\(y\) 是不是好人。
如果 \(x\) 是好人他会如实回答,否则会按一定策略任意回答。
问你能否判断哪些人是好人,并交互。

Solution1.

首先考虑无解情况。
如果 \(a\le b\),我们取出一个 \(b\) 的集合装成好人,肯定分不清。
否则就一定有解,证明通过构造。
首先,我们分别问 \(x\) \(y\) 的身份和问 \(y\) \(x\) 的身份。
分类讨论:

  1. 互喷:那肯定有一个是坏蛋,可以先不管最后找到一个诚实的人再问两遍
    用了 \(4\) 步删除了一个人
  2. 互捧:要么两个都是坏蛋,要么都是诚实,可以少掉一个人
  3. 一捧一喷:被喷的肯定不是好人是坏蛋

递归时 \(a\le b\) 的性质不会变。
然后就做完了。

发现这个做法有点 shit,找到一个诚实的人很难。
反正博主是直接写挂了。

Solution2.

考虑维护一个栈,每次询问栈顶 新元素的身份。
如果是好人就说明身份相同,否则就肯定有一个坏人。
如果是好人的话就直接入栈,否则弹栈。
这样构成了一个“猜疑链”,如果有一个人是好人那后面所有人都是好人。
这样就找到了唯一好人。

Coding2.

点击查看代码
//是啊……你就是那只鬼了……所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=1;
	for(;c>='0'&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	if(f) x=-x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
int n,a,b,st[100005],tp,rs[100005];
#ifndef ONLINE_JUDGE
int limit=0;char debug[100005];mt19937 rnd(time(0));
#endif
inline char qry(int x,int y)
{
#ifdef ONLINE_JUDGE
	printf("? %d %d\n",x-1,y-1),fflush(stdout);
	char ch[4];scanf("%s",ch);return *ch=='Y';
#else
	limit++;if(debug[x]) return debug[y];
	else return 0;
#endif
}
int main()
{
#ifdef ONLINE_JUDGE
	read(a,b),n=a+b;
#else
	scanf("%s",debug+1),n=strlen(debug+1);
	for(int i=1;i<=n;i++) debug[i]^=48;
	for(int i=1;i<=n;i++) a+=debug[i],b+=!debug[i];
#endif
	if(a<=b) return puts("Impossible"),0;
	for(int i=1;i<=n;i++) if(!tp) st[++tp]=i;
	else if(qry(st[tp],i)) st[++tp]=i;else tp--;
	int nw=st[tp];rs[nw]=1;
	for(int i=1;i<=n;i++) if(i^nw) rs[i]=qry(nw,i);
#ifndef ONLINE_JUDGE
	printf("cnt = %d\nlimit = %d\n",limit,n*2);
	for(int i=1;i<=n;i++) if(rs[i]^debug[i]) return puts("Fail"),0;
	return puts("Succeed"),0;
#else
	printf("! ");for(int i=1;i<=n;i++) putchar(rs[i]^48);
	return putchar('\n'),fflush(stdout),0;
#endif
}