NoiminのNoise

競技プログラミング (多め) とWeb (たまに) ,自然言語処理 (ブログではまだ)。数式の書き方を一気に KaTeX に変えようとして記事を全削除してインポートし直すなどしたので,過去にブックマークされた記事は URL が変わってしまっている可能性があります…….

Google Code Jam 2019 Round 1B Draupnir

問題 / 公式解説: https://codingcompetitions.withgoogle.com/codejam/round/0000000000051706/0000000000122837

問題概要

(インタラクティブ問題)

"X-day ring" ( $ 1 \leq X \leq 6 $ ) というものがある.X-day ring は X 日ごとにもう 1 つの X-day ring を生成する.例えば 0 日目に 3-day ring が 1 つあったら,3 日目,6 日目の 3-day ring の数はそれぞれ 2 つ,4 つになる.

0日目に存在する 1-day ring, 2-day ring, ..., 6-day ring の数を $ R_1, R_2, \cdots, R_6 $ (ただし $ 1 \leq i \leq 6, 0 \leq R_i \leq 100 $ )を求めよ.ただし,そのために以下の質問を W 回 (Visible では $ W = 6$, Hidden では $ W = 2$) まで行うことができる.

入力: D ( $ 1 \leq D \leq 500 $ )

出力: D 日目に存在する ring の数の総計を $ 2^{63} $ で割ったあまり

解法概要

Hidden の場合,$ W = 2$ なので1回の質問で 6 種類中 3 種類の ring の個数を特定することを考えてみる.

ring の増え方に着目すると,D 日目の i-day ring の個数は $ R_i \times 2^\frac{D}{i} $ の形で表すことができる.また,$ R_i $ の最大値は 10 進数で 100,つまり7bit の値 (1100100) であることから,$ \frac{D}{i-1} - \frac{D}{i} \geq 7 $ となるような D を入力すれば,返ってきた値の $ \frac{D}{i} $ bit 目から 7bit ぶんのみを取り出した値が $ R_i $ である.

63bit を超えた値は $ 2^{63} $ で割ったあまりには影響しないが $ R_i $ を特定する計算には使えなくなってしまうことと, $ \frac{D}{i-1} - \frac{D}{i} \geq 7 $ という条件に注意しながら,$ 1 \leq i \leq 3 $ の $ R_i $ を特定するための質問(私のソースコードでは $ D = 50 $) と $ 4 \leq i \leq 6 $ の $ R_i $ を特定するための質問 (私のソースコードでは $ D = 220 $) の $ R_i $ を特定するための質問を 1 回ずつすれば,2回の質問で全ての種類の ring の個数を特定することができる.

ただし $ R_3 $ を求める時だけ注意が必要である. $ D = 50 $ のとき,

$ i = 1 $ なら $ \frac{D}{i} = 50 $

$ i = 2 $ なら $ \frac{D}{i} = 25 $

$ i = 3 $ なら $ \frac{D}{i} = 16 $

$ i = 4 $ なら $ \frac{D}{i} = 12 $

$ i = 5 $ なら $ \frac{D}{i} = 10 $

$ i = 6 $ なら $ \frac{D}{i} = 8 $

となっている.つまり,ただ 7bit ぶんマスクするだけでは $ R_3 $ を求めることはできない.例えば 16bit 目には $ i = 3, 4, 5 $ のときの $ R_i $ の情報が混ざってしまっているためである.そのため,$ R_3 $ を求める際には,D日目の ring の個数もともとの値から$ R_4 $ のぶんと $ R_5 $ のぶんを引いておく必要がある.

ソースコード

#include <iostream>
#include <vector>

using namespace std;

typedef unsigned long long ull;

const int Q1 = 220;
const int Q2 = 50;

int T, W;

void solve() {
    vector<ull> rings(6, 0);
    ull ret, mask = (1uLL << 7) - 1uLL;
    cout << Q1 << endl;
    cin >> ret;
    for(int i=3;i<6;++i) {
        rings[i] = (ret >> (Q1/(i+1))) & mask;
    }

    cout << Q2 << endl;
    cin >> ret;
    for(int i=0;i<2;++i) {
        rings[i] = (ret >> (Q2/(i+1))) & mask;
    }
    rings[2] = ((ret - (rings[3] << (Q2/4)) - (rings[4] << (Q2/5))) >> (Q2/3)) & mask;

    for(int i=0;i<5;++i) cout << rings[i] << " ";
    cout << rings[5] << endl;
    cin >> ret;
    if(ret == -1) exit(0);
}

int main() {
    cin >> T >> W;
    for(int t=0;t<T;++t) {
        solve();
    }
    
    return 0;
}

所感

$ R_3 $ 以外は比較的すぐわかったけど $ R_3 $ の求め方に気づくまでかなり時間を要してしまった.