公式

H - Bad Juice 解説 by leaf1415


呼ぶ人数が \(M\) 人のとき、翌日に各友人がお腹を壊したかの情報 \(S\) が取りえるのは高々 \(2^M\) 通りなので、 \(N\) 通り考えられる腐ったジュースの番号を \(S\) の情報から一意に識別するには \(2^M \geq N\) 、つまり、\(M \geq \left\lceil \log_2 N \right\rceil\) が必要です。

反対に、呼ぶ人数は \(b \coloneqq \left\lceil \log_2 N\right\rceil\) 人で十分です。実際に、以下に述べる手順によって \(b\) 人の友人を利用して腐ったジュースを特定できます。

便宜上、ジュースの番号と友人の番号を \(0\) から起算し、それぞれ \(0, 1, 2, \ldots, N-1\)\(0, 1, 2, \ldots, b-1\) とします。 このとき、\(2^b-1 \geq N-1\) なので、すべてのジュースの番号は \(b\) 桁の二進数で表現できます。 その各桁を最下桁から順に \(0, 1, 2, \ldots, b-1\) ビット目と番号づけます。 そして、下記の通りに各友人に飲ませるジュースの組み合わせを決定します。

友人 \(i = 0, 1, 2, \ldots, b-1\) とジュース \(j = 0, 1, 2, \ldots, N-1\) の各組 \((i, j)\) について、 \(i\) 番目の友人には、ジュースの番号 \(j\) の二進数表現における \(i\) ビット目が立っているとき、かつ、そのときに限り \(j\) 番目のジュースを飲ませる。

このとき、腐ったジュースの番号 \(X\) の二進数表現の \(0 ,1, 2, \ldots, b-1\) 桁目と、文字列 \(S\)\(0, 1, 2, \ldots, b-1\) 文字目(先頭の文字を \(0\) 文字目とする)が一致し、\(X\)\(S\) から一意に特定できます。

以下に、C++ 言語による本問題の正解例を記載します。

#include <iostream>
#include <vector>
using namespace std;

int main(void){
  int n;
  cin >> n;
  
  int b = 0;
  while((1<<b) < n) b++;
  cout << b << endl;
  
  for(int i = 0; i < b; i++){
    vector<int> avec;
    for(int j = 0; j < n; j++) if(j & (1<<i)) avec.push_back(j+1);
    cout << avec.size() << " ";
    for(auto a : avec) cout << a << " ";
    cout << endl;
  }
  
  string s;
  cin >> s;
  
  int ans = 0;
  for(int i = 0; i < b; i++) if(s[i] == '1') ans |= 1<<i;
  cout << ans+1 << endl;
  
  return 0;
}

投稿日時:
最終更新: