けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

全国統一プログラミング王決定戦 エキシビジョン H - 8^kゲーム (500 点)

きれい

問題へのリンク

問題概要

 N 枚のコインがあります。高橋君と青木君は、以下の操作を高橋君から始めて交互に繰り返します。

  • 0 以上の整数  k を選び、コインを  8^{k} 枚取り除く。

先に操作を行えなくなった者の負けです。両者が最適に行動するとき、どちらが勝つでしょうか?

制約

  •  1 \le N \le 10^{18}

考えたこと

もしこれが ARC-D 問題に出題されたとして、冷静に「実験しよう」と思えるだろうか...

実験すると確かに見えて来る。実験するとしたら、例えば以下のように書ける (このようなゲームを実験するときの書き方についてはこの記事などに書いた)。

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

int dp[110000];
int iswin(long long n) {
    if (n == 0) return 0;
    if (dp[n] != -1) return dp[n];
    
    long long eight = 1;
    while (eight <= n) {
        if (!iswin(n - eight)) return dp[n] = 1; // 相手に「負け」を渡せたら勝ち
        eight *= 8;
    }
    return dp[n] = 0;
}

int main() {
    memset(dp, -1, sizeof(dp));
    for (int i = 0; i <= 100; ++i) {
        cout << i << ": " << iswin(i) << endl;
    }
}

これを出力すると、以下のようになる

0: 0
1: 1
2: 0
3: 1
4: 0
5: 1
6: 0
7: 1
8: 1
9: 0
10: 1
11: 0
12: 1
13: 0
14: 1
15: 0
16: 1
17: 1
18: 0
19: 1
20: 0
21: 1
22: 0
23: 1
24: 0
25: 1
26: 1
27: 0
28: 1
29: 0
30: 1
31: 0
32: 1
33: 0
34: 1
35: 1
36: 0
37: 1
38: 0
39: 1
40: 0
41: 1
42: 0
43: 1
44: 1
45: 0
46: 1
47: 0
48: 1
49: 0
50: 1
51: 0
52: 1
53: 1
54: 0
55: 1
56: 0
57: 1
58: 0
59: 1
60: 0
61: 1
62: 1
63: 0
64: 1
65: 0
66: 1
67: 0
68: 1
69: 0
70: 1
71: 1
72: 0
73: 1
74: 0
75: 1
76: 0
77: 1
78: 0
79: 1
80: 1
81: 0
82: 1
83: 0
84: 1
85: 0
86: 1
87: 0
88: 1
89: 1
90: 0
91: 1
92: 0
93: 1
94: 0
95: 1
96: 0
97: 1
98: 1
99: 0
100: 1

じっと眺めると、周期 9 で、

0, 1, 0, 1, 0, 1, 0, 1, 1

を繰り返していることがわかる。証明は後に回すとして、この通りに実装すると通る。

#include <iostream>
using namespace std;

int ans[9] = {0, 1, 0, 1, 0, 1, 0, 1, 1};

int main() {
    long long n;
    cin >> n;
    if (ans[n%9]) cout << "Win" << endl;
    else cout << "Lose" << endl;
}

証明

コイン枚数を 9 で割ったあまりが、0, 2, 4, 6 の状態が「負けの盤面」であり、1, 3, 5, 7, 8 の状態が「勝ちの番目」であるということになる。以下の 2 つを示せばいい:

  1. 「負けの盤面」を渡されたとき、何をやっても「勝ちの盤面」にしか遷移できず、必ず相手に「勝ちの盤面」を渡してしまう

  2. 「勝ちの盤面」を渡されたとき、上手く手を打つことで「負けの盤面」にして相手に渡すことができる

1. について

 8^{k} ≡ (-1)^{k} ≡ ±1 ({\rm mod}. 9) であるから、 n ≡ 0, 2, 4, 6 ({\rm mod}. 9) の状態からどのような手を打っても、

 n - 8^{k} ≡ 1, 3, 5, 7, 8 ({\rm mod}. 9)

のいずれかになってしまう。

2. について

 n ≡ 1, 3, 5, 7 ({\rm mod}. 9) のときは、石を 1 個とることで、 n - 1 ≡ 0, 2, 4, 6 ({\rm mod}. 9) になるので OK。

 n ≡ 8 ({\rm mod}. 9) のときは、まず  n \ge 8 であることが保証されるので、 8 個とることができる。そして  8 個とることで、 n - 8 ≡ n + 1 ≡ 0 ({\rm mod}. 9) になるので OK。