AT_arc070_d [ARC070F] HonestOrUnkind
AT_arc070_d [ARC070F] HonestOrUnkind
题目描述
これはインタラクティブな問題です。
シカのAtCoDeerくんは $ 0 $ ~ $ N-1 $ の番号がついた $ N $ 人の人が集まっているのを見つけました。 この内 $ A $ 人は正直者で、残りの $ B(=N-A) $ 人は不親切な人です。 $ N $ 人の人は全員、誰が正直者で、誰が不親切な人かを把握しています。 一方、AtCoDeerくんは、正直者が $ A $ 人いて不親切な人が $ B $ 人いることしか知りません。 そこで、AtCoDeerくんはこれらの $ N $ 人に質問をして、正直者を全員特定しようとしています。 一回の質問では、AtCoDeerくんは、$ 0≦a,b≦N-1 $ なる $ a $, $ b $ を選んで、$ a $ さんに「$ b $ さんは正直者ですか?」という質問をします。
正直者は、質問に対し常に Yes か No の正しい答えを返します。 一方、不親切な人は、質問に対し Yes か No のどちらかを恣意的に選んで返します。 つまり、常に嘘をついたり、半分の確率でランダムに答えるといった単純なアルゴリズムではない可能性があります。
AtCoDeerくんは高々 $ 2N $ 回質問をすることができます。質問は順番に行われ、以前の質問の結果から次の質問を決めることが出来ます。
正直者を全員特定してください。 不可能な場合(正確には、どのような $ 2N $ 回の質問をしようと、不親切な人たちがうまく返答することで、正直者の集合としてありうるものが2つ以上存在するようにできる場合)は、その旨を出力してください。
Input & Output Format
最初に、標準入力から $ A $ と $ B $ が以下の形式で与えられる:
$ A $ $ B $
もし特定が不可能な場合は、即座に次のように出力し、プログラムを終了しなければならない。:
Impossible
それ以外の場合は、次に、クエリを質問する。 各クエリは、標準出力に以下の形式で出力されなければならない:
? $ a $ $ b $
ここで $ a $ と $ b $ は $ 0 $ 以上 $ N-1 $ 以下の整数でなければならない。
次に、クエリの答えが標準入力から以下の形式で与えられる:
$ ans $
ここで $ ans $ は Y または N である。 Y のときは質問の答えが Yes であることを、N のときは No であることを表す。
最後に、答えを以下の形式で出力しなければならない:
! $ s_0s_1...s_{N-1} $
ここで $ s_i $ は $ i $ 番の人が正直者なら 1、不親切な人なら 0 でなければならない。
说明/提示
制約
- $ 1≦A,B≦2000 $
ジャッジ
- 出力のあと、標準出力を flush しなければならない。 そうでないときは
TLEの可能性がある。 - 答えを出力した後、プログラムをすぐに終了しなければならない。そうでないときの挙動は定義されていない。
- 出力の答えが間違っている場合の挙動は定義されていない (
WAとは限らない)。
サンプル
このサンプルでは $ A\ =\ 2 $, $ B\ =\ 1 $ で、答えは 101 である。
| Input | Output |
|---|---|
| 2 1 | |
| ? 0 1 | |
| N | |
| ? 0 2 | |
| Y | |
| ? 1 0 | |
| Y | |
| ? 2 0 | |
| Y | |
| ? 2 2 | |
| Y | |
| ! 101 |
次のサンプルでは A=1, B=2 で、答えは Impossible である。
| Input | Output |
|---|---|
| 1 2 | |
| \(Impossible\) |
题解
如果 \(a≤b\) 也就是好人没坏人多:只要其中 \(a\) 个坏人说自己阵营的都是好人,对方阵营的都是坏人,坏人就变得和好人一样了,显然无法分辨。
首先要先找到一个好人。为了实现这个,我们考虑一种特别神仙的做法:
维护一个类似单调栈的玩意,它上面是好人,下面是坏人。对于每一个 \(u\),做询问操作 \(query(top,u)\)。
分类讨论。有四种情况:
- 栈顶是好人并且回答 \(Yes\)。那么这个人是好人,放进栈里仍然符合栈的性质。
- 栈顶是好人并且回答 \(No\)。那么这个人是坏人。
- 栈顶是坏人并且回答 \(Yes\)。那么我们无法知道这个人是谁,但是由于此时栈没有好人(顶部是坏人,说明没有好人)直接放进去也是没问题的。
- 栈顶是坏人并且回答 \(No\)。这个人可能是好人也可能是坏人。
注意到我们对 \(2\) 与 \(4\) 号情况没有好方法。观察一下,发现一个很重要的性质:栈顶和这个人至少有一个是坏人。
这里面有坏人啊,那我们当然要草掉他,于是把两个人都扔掉。
- 若有一个坏人,\((a,b)→(a−1,b−1)\),仍然符合 \(a>b\)。
- 若有两个坏人,\((a,b)→(a,b−2)\),仍然符合 \(a>b\)。
综上,操作完后,\(a>b\)。又因为我们操作完了,所以 \(b=0\),也就是说栈里面全是好人。
于是我们完成了我们的目标:找到了一个好人。
回过头来看一下流程。显然只用去了 \(n\) 次操作,剩下 \(n\) 次。
那就很简单了,抓这个人拷问 \(n\) 次就能知道全部人的身份了。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int a,b;
bool ans[N];
char s;
stack<int> stk;
bool query(int x,int y){
cout<<"? "<<x<<' '<<y<<'\n';
cout.flush();
cin>>s;
return s=='Y';
}
int main(){
cin>>a>>b;
if(a<=b){
cout<<"Impossible";
cout.flush();
return 0;
}
for(int i=0;i<a+b;i++){
if(stk.empty()) stk.push(i);
else{
int top = stk.top();
if(!query(top,i)) stk.pop();
else stk.push(i);
}
}
int flag = stk.top();
for(int i=0;i<a+b;i++) ans[i] = query(flag,i);
cout<<"!";
for(int i=0;i<a+b;i++) cout<<ans[i];
cout.flush();
return 0;
}

浙公网安备 33010602011771号