DDCC2020予選: E - Majority of Balls

問題概要

 2N 個のボールがあり, N個は赤色で N個は青色である。

 2N 個 のボールのうちの N個を指定してその N個のボールのうちどちらの色の方が多いか,という質問を  210 回まですることができる。

このとき, 2N 個のボールの色をすべて当てよ。

(問題文は https://atcoder.jp/contests/ddcc2020-qual/tasks/ddcc2020_qual_e に記載されている。)

制約

  •  1 \leq N \leq 99
  •  Nは奇数
  •  210 回まで質問ができる

アイディア

 f(i) := (ボールi, i+1, \cdots, i+N-1 を指定したときのクエリの返答) とする。

ここで  f(1) = f(N+1) = c とすると,ボール 1,2,3,\cdots,N,N+1,N+2,\cdots,2N 2N個のボールでも色 cのボールが多いことになってしまうので矛盾する。よって f(1) \neq f(N+1) である。

このことから, f(p) \neq f(p+1)となる p 1,2,\cdots,Nに存在する(もし \forall pに対して f(p)=f(p+1)であるとすると f(1)=f(2)=\cdots=f(N)=f(N+1)となり f(1) \neq f(N+1)と矛盾)。

このとき, f(p) \neq f(p+1)なので, f(p)=cとすると p, p+1, \cdots, p+N-1に含まれる色 cのボールの個数は \dfrac{N+1}{2}以上 N以下であり,よって p+1, p+2, \cdots, p+N-1, p+Nに含まれる色 cのボールの個数は \dfrac{N+1}{2}以上 N+1以下となる(ボール p+N c色ならば色 cのボールの個数は 1つ増え,それ以外の場合は変わらないため)。

次に, f(p+1)=\bar{c}とすると(ただし \bar{c} cではない方の色)とすると, p+1, p+2, \cdots, p+Nに含まれる色 cのボールの個数は 0以上 \dfrac{N-1}{2}以下となるので, p, p+1, p+2, \cdots, p+Nに含まれる色 cのボールの個数は 0以上 \dfrac{N+1}{2}以下となる(ボール p c色ならば色 cのボールの個数は 1つ増え,それ以外の場合は変わらないため)。

以上から, p, p+1, \cdots, p+Nに含まれる色 cのボールの個数は \dfrac{N+1}{2}以上 \dfrac{N+1}{2}以下となり,つまり p, p+1, \cdots, p+Nに含まれる cのボールの個数は \dfrac{N+1}{2}個,よって区間 [p,p+N]のボールは赤色と青色のボールを同数含む。

区間 [p,p+N]は両色のボールを同数ずつ含むので,この区間内のボールに関しては自身を除いた N個のボールをそれぞれ質問で投げればよい。

さらに, p p+Nのボールの色は違うので(同じであれば f(p)=f(p+1)となるので矛盾), [1,p]  \cup [p+N,2N ]についても両色のボールを同数ずつ含むといえるので,この区間内のボールに関しても自身を除いた N個のボールをそれぞれ質問で投げればよい。

これが基本方針である。

高速化

 f(p) \neq f(p+1) となる  pを探すところで二分法を使えばこの部分での質問は log_2 N回程度におさまり,全体的な質問回数が 2N+log_2 N回程度となる。

実装

素直に実装するだけ。

コード

#include "bits/stdc++.h"
using namespace std;

int N,RB='R'+'B';

string reply;
char getreply(){
    cin >> reply;
    return reply[0];
}

void sendquestion(int s){
    int i;
    cout << "?";
    for(i=0; i<N; ++i){
        cout << " " << s+i;
    }
    cout << endl; cout.flush();
}

char c,ans[225];

int main(void){
    int i,j,p,l,r,m;
    cin >> N;
    l = 1; r = N+1;
    sendquestion(1); c = getreply();
    while(r-l>1){
        m = (l+r)/2;
        sendquestion(m);
        if(c == getreply()){
            l = m;
        }else{
            r = m;
        }
    }
    p = l;
    for(i=p; i<=p+N; ++i){
        cout << "?";
        for(j=p; j<=p+N; ++j){
            if(i==j){
                continue;
            }
            cout << " " << j;
        }
        cout << endl; cout.flush();
        ans[i-1] = RB-getreply();
    }
    for(i=1; i<=N*2; ++i){
        if(p<=i && i<=p+N){
            continue;
        }
        cout << "?";
        for(j=1; j<=p; ++j){
            if(i==j){
                continue;
            }
            cout << " " << j;
        }
        for(j=p+N; j<=N*2; ++j){
            if(i==j){
                continue;
            }
            cout << " " << j;
        }
        cout << endl; cout.flush();
        ans[i-1] = RB-getreply();
    }
    cout << "! ";
    for(i=0; i<N*2; ++i){
        cout << ans[i];
    }
    cout << endl; cout.flush();
    return 0;
}

(提出したものはこちら)

感想

橙パフォうれしい