Day 179-Q2 — 2026-10-15 | オクタルゲームの周期性定理(Guy–Smith の証明書「$n_0\le n<2n_0+p+t$ で一致すれば以後ずっと一致」を使い、Kayles などの Grundy 数列の周期と前周期を確定させて $10^{18}$ 個の山を判定する)

2026-10-15 赤色 Master (Phase 8+) ★★★★★★★★★ ゲーム理論(焦点帯 M1)。「Grundy 数を 200 個くらい並べて周期っぽいところを探す」は競プロでよくやるが、Kayles の最後の例外は $n=70$ で、Q2 の乱択コードには前周期 775 のものもある。どこまで計算すれば周期が証明されるかを、有限の計算で言い切る定理を実装する

問題

オクタルゲーム $0.d_1d_2\dots d_t$(各 $d_k$ は 0〜7、$d_t\ne0$)が与えられる。石の山がいくつかあり、2 人が交互に次の手を指す。

  • 山を 1 つ選び、そこから $k$ 個($1\le k\le t$)取る。取った後に残る石の扱いは $d_k$ のビットで決まる - ビット 1: 山を取り切ってよい(残り 0 個のとき) - ビット 2: 残りを 1 つの空でない山として残してよい - ビット 4: 残りを 2 つの空でない山に分けてよい
  • $d_k$ が許さない手は指せない。手が指せないプレイヤーの負け

山が $N$ 個あり大きさは $h_1,\dots,h_N$ である。1 行目に、1 つの山の Grundy 数列 $g(n)$ の最小周期 $p$ と、その周期での最小の前周期 $n_0$($n\ge n_0$ で $g(n+p)=g(n)$ となる最小の $n_0$)を出力せよ。2 行目に、先手が勝つなら First、後手が勝つなら Second を出力せよ。

入力形式

0.d_1d_2...d_t N
h_1 h_2 ... h_N

制約

#制約
1$1\le t\le8$、$d_t\ne0$
2$1\le N\le2\times10^5$、$1\le h_i\le10^{18}$
3ある $n_0,p$ について $2n_0+2p+t\le4096$ かつ「$n_0\le n<2n_0+p+t$ で $g(n+p)=g(n)$」が成り立つことが保証される

入出力例

入力例1

0.77 3
70 82 5

出力例1

12 71
Second

(Kayles。$g(70)=6$、$g(82)=2$、$g(5)=4$ で XOR は 0。周期 12 は $n=71$ から始まり、$70$ が最後の例外。「先頭から周期 12」と思うと $g(70)=g(82)=2$ として先手勝ちと誤る)

入力例2

0.137 2
1000000000000000000 3

出力例2

34 52
First

(Dawson's chess。$g(10^{18})=g(52+(10^{18}-52)\bmod34)=g(66)=7$、$g(3)=2$)

入力例3

0.31511562 2
774 803

出力例3

29 775
First

(本セッションで乱択コードから見つけた前周期の長い例。$g(774)=32$、$g(803)=15$。周期 29 は $n=775$ からで、$774$ は最後の例外。3 例とも全探索と一致)

背景と出典

  • オクタルゲームの記法と Kayles(0.77)・Dawson's chess(0.137)・Dawson's Kayles(0.07)の周期は、Berlekamp–Conway–Guy『Winning Ways』で扱われていると記憶している(本セッションでは原典を開いていない)。本問の出力 Kayles $(12,71)$、0.137 $(34,52)$、0.07 $(34,53)$ は本セッションの計算値
  • 周期性定理(Guy–Smith 1956 と記憶、未確認): $t$ を取れる最大の個数とし、$n_0\le n<2n_0+p+t$ のすべてで $g(n+p)=g(n)$ なら、すべての $n\ge n_0$ で成り立つ。証明の筋道は Step 3
  • 0.6・0.16・0.106 などは周期が分かっていない(あるいは極端に長い)ことで有名と記憶している。本セッションでも $n<16384$ までで証明書が出なかった(0.007・0.37 も同じ)
  • 本問の問題文・制約・入力例は自作

概念図

Kayles(0.77)の g(n)、12 個ずつ折り返し。赤枠 = g(n) ≠ g(n+12)(周期から外れる位置) 0–11 0 1 2 3 1 4 3 2 1 4 2 6 12–23 4 1 2 7 1 4 3 2 1 4 6 7 24–35 4 1 2 8 5 4 7 2 1 8 6 7 36–47 4 1 2 3 1 4 7 2 1 8 2 7 48–59 4 1 2 8 1 4 7 2 1 4 2 7 60–71 4 1 2 8 1 4 7 2 1 8 6 7 72–83 4 1 2 8 1 4 7 2 1 8 2 7 84–95 4 1 2 8 1 4 7 2 1 8 2 7 最後の例外は n = 70 n0 = 71 から周期 12 46〜56 は一致していても 57, 58, 70 でまた外れる 証明書:n0 ≤ n < 2·n0 + p + t = 71 ≤ n < 156 で一致すれば 以後ずっと一致(t = 2) 必要な計算:n < 168

ヒント(クリックで段階的に開示)

ヒント1(方向性)

1 つの山の Grundy 数 $g(n)$ を $n$ の小さい順に計算する。山を 2 つに分ける手の値は $g(i)\oplus g(m-i)$。あとは $g$ が周期的になる $n_0,p$ を見つければ、$10^{18}$ の山も $g(n_0+(h-n_0)\bmod p)$ で引ける。問題は「どこまで見れば周期と言い切れるか」。

ヒント2(アプローチ)

$n$ の行き先は $n-k$(1 山)か $(i,\ n-k-i)$(2 山)。2 山のうち小さい方は $\lfloor (n-k)/2\rfloor$ 以下。$n$ が十分大きいと、大きい方の山 $n-k-i$ は $n_0$ 以上(周期の領域)に入る。小さい方が $i<n_0+p$ でも大きい方は周期で 1 つずらせる。

ヒント3(誘導)
  • $n\ge2n_0+p+t$ を帰納法で示す。$n$ と $n+p$ の行き先を「大きい方の山を $p$ ずらす」ことで 1 対 1 に対応させる。大きい方の山は $n_0$ 以上かつ $n$ 未満なので、帰納法の仮定で値が等しい
  • 実装: $B=64,128,\dots$ と倍々に $g$ を伸ばし、各 $p$ について後ろから最初の不一致で $n_0$ を決め、$2n_0+p+t+p\le B$ なら証明書成立
  • 見つかった $p$ の約数 $q$ のうち、$n_0$ からの 1 周期が $q$ で回るものが最小周期。前周期は $q$ について改めて後ろから最初の不一致で決める

模範解答 (Python)

模範解答を表示する(まず自力で解いてから)
import sys
from operator import xor


def extend(g, code, B):
    """g[0..B-1] まで伸ばす(オクタルゲーム 0.d1d2...)"""
    t = len(code)
    for n in range(len(g), B):
        s = set()
        for k in range(1, min(t, n) + 1):
            d = code[k - 1]
            m = n - k                              # 取った後に残る石
            if d & 1 and m == 0:
                s.add(0)                           # 山を取り切る
            if d & 2 and m > 0:
                s.add(g[m])                        # 1 山を残す
            if d & 4 and m >= 2:                   # 2 つの空でない山 (i, m-i) に分ける
                h = m // 2
                s.update(map(xor, g[1:h + 1], g[m - 1:m - h - 1:-1]))
        v = 0
        while v in s:
            v += 1
        g.append(v)


def certificate(g, t):
    """周期性定理: g(n+p)=g(n) が n0 <= n < 2*n0+p+t で成り立てば以後ずっと成り立つ"""
    B = len(g)
    for p in range(1, B):
        last = -1
        for n in range(B - p - 1, -1, -1):         # 後ろから最初の食い違い
            if g[n + p] != g[n]:
                last = n
                break
        n0 = last + 1
        if 2 * n0 + p + t + p <= B:                # 確かめるべき範囲を計算済み
            return p, n0
    return None


def main():
    data = sys.stdin.buffer.read().split()
    code = [int(c) for c in data[0].decode().split(".")[1]]
    t = len(code)
    n = int(data[1])
    hs = [int(x) for x in data[2:2 + n]]
    g = [0]
    B = 64
    while True:
        extend(g, code, B)
        cert = certificate(g, t)
        if cert:
            break
        B *= 2                                     # 倍々に伸ばして証明書を探す
    p, n0 = cert
    # 最小周期へ: p の約数 q のうち、n0 からの 1 周期が q で回るもの(n0 以降は p 周期なのでこれで十分)
    for q in range(1, p + 1):
        if p % q == 0 and g[n0 + q:n0 + q + p] == g[n0:n0 + p]:
            p = q
            break
    n0 = 0                                         # その周期での最小の前周期
    for i in range(len(g) - p - 1, -1, -1):
        if g[i + p] != g[i]:
            n0 = i + 1
            break
    G = 0
    for h in hs:
        G ^= g[h] if h < len(g) else g[n0 + (h - n0) % p]
    print(p, n0)
    print("First" if G else "Second")


main()

計算量: $g$ を $B$ 個作るのに $O(B^2t)$(分ける手の XOR を map(xor, ...) で C の速さで回す)、証明書探しは各 $p$ で後ろから最初の不一致まで。山の判定は $O(N)$。$N=2\times10^5$ の $10^{18}$ 級の山で、0.31511562($B=2048$ で証明書)0.07 s、0.77777777 0.06 s。証明書が出ない 0.44444444 を $B=4096$ まで計算すると 0.41 s(Python 3.14.8、Apple M5 Pro、起動込み)。

Step-by-Step 解説

11 つの山の Grundy 数

Sprague–Grundy の定理で、山の和の値は各山の $g(h_i)$ の XOR。$g(n)$ は「$n$ から 1 手で行ける局面の値」の mex で、2 山に分ける手の値は $g(i)\oplus g(m-i)$。$i\le m-i$ だけを見れば十分。

2周期を「見つける」だけでは足りない

Kayles(0.77)の $g$ は $n<96$ でこう並ぶ(12 個ずつ)。* は $g(n)\ne g(n+12)$ の位置:

行$g(n)$
0–110* 1 2 3* 1 4 3 2 1 4 2* 6*
12–234 1 2 7* 1* 4 3* 2 1 4* 6 7
24–354 1 2 8* 5* 4 7 2 1 8 6* 7
36–474 1 2 3* 1 4 7 2 1 8* 2 7
48–594 1 2 8 1 4 7 2 1 4* 2* 7
60–714 1 2 8 1 4 7 2 1 8 6* 7
72–834 1 2 8 1 4 7 2 1 8 2 7

$n=46..56$ は一致しているのに、その先の 57, 58, 70 でまた外れる。「しばらく一致したから周期」は危ない。

3周期性定理

$n\ge2n_0+p+t$ で $g(n+p)=g(n)$ を、それより小さい $n$ で成り立つと仮定して示す。方針は「大きい方の山を $p$ だけずらす」こと。

  • $n$ の行き先 $(a,c)$($a\le c$、$a+c=n-k$、1 山なら $a=0$): $c\ge(n-k)/2\ge(2n_0+p)/2\ge n_0$ かつ $c<n$ なので、仮定から $g(c+p)=g(c)$。$(a,c+p)$ は $n+p$ の行き先
  • $n+p$ の行き先 $(a,b)$($a\le b$、$a+b=n+p-k$): $b\ge(n+p-k)/2$ なので

$$b-p\ \ge\ \frac{n-p-k}{2}\ \ge\ \frac{2n_0+p+t-p-t}{2}\ =\ n_0$$

で $b-p<n$、仮定から $g(b)=g(b-p)$。$(a,b-p)$ は $n$ の行き先

行ける値の集合が一致するので mex も一致する。$t$(取る個数の最大)と $p$ は、この不等式で $b-p\ge n_0$ を保つために要る。山の個数が変わる境目($n_0=0$ で $b-p=0$ など)の細部は本セッションでは詰めていない。

4倍々に伸ばして証明書を探す

$B=64$ から始め、$p=1,2,\dots$ について後ろから最初の不一致位置 $+1$ を $n_0$ とし、$2n_0+p+t$ 未満の $n$ について $n+p<B$(つまり $2n_0+2p+t\le B$)なら成立。なければ $B$ を倍にする。$B$ を倍にするたびに $O(B^2t)$ を払い直すので、全体は最後の $B$ の定数倍。

5最小周期と最小の前周期

証明書の $p$ は最小とは限らない。真の最小周期 $q$ は $p$ を割り、$n_0$ 以降はすでに $p$ 周期なので、$[n_0,\ n_0+p)$ の 1 周期を $q$ ずらして一致すれば以後ずっと $q$ 周期。前周期は $q$ について後ろから最初の不一致で決め直す($n_0$ 以降に不一致は無いので、計算済みの範囲で正確に決まる)。

迷った他案と採らなかった理由

案計算量採否理由
先頭 200 個を計算し、後半 $[100,200)$ で一致する最小周期を使う$O(200^2t)$不採用(誤り)証明書が出た 146 コード中 14 コードで、$n<20000$ のどこかの値を誤る(実測)
全探索(山の多重集合でメモ化)指数検算用のみ山 1〜3 個・大きさ 14 以下で 1500 入力の勝敗を確認
numpy で分ける手の XOR をベクトル化$O(B^2t)$不採用この環境の Python 3.14 に numpy が無い。map(xor, ...) で $B=4096$ でも 0.41 s に収まった
疎な値の手法(rare/common 値を分ける、Gangolli–Plambeck と記憶)平均で速い不採用0.6 のような長い計算向け。$B\le4096$ では不要
周期性定理の証明書を倍々で探す(本解)$O(B^2t)$採用判定は 0.07 s

前提が変わったら

  • $d_0$(先頭の桁)が 4 など、取らずに分ける手があるゲーム(例: 4.007)では、$t=0$ の扱いを含めて定理の形が変わる(「取る個数」の最大で $t$ を決める)。本問は $0.$ 始まりだけ
  • 証明書が出ないコード(0.6 など)では、この方法では何も言えない。周期を仮定して答えるのは推測であり、問題として成立しない
  • 数列が算術周期的($g(n+p)=g(n)+c$、例: 一部のビット 4 を含むゲーム)なら、別の定理(算術周期性の証明書)が要る。本セッションの乱択 300 コードのうち 154 コードで $B\le4096$ の証明書が出なかったのは、この種のものや長い前周期のものが混ざっているためと考えるが、内訳は調べていない

よくあるミス

ミス原因正しい書き方
2 山に分ける手で空の山を許すビット 4 とビット 2 を混同ビット 4 は「2 つの空でない山」
$n$ 個すべて取る手を、ビット 1 が無いのに許す「$k$ 個取れる」とだけ読む残り 0 個はビット 1、1 山はビット 2
証明書の範囲を $2n_0+p$ までにする$t$ を忘れる$n_0\le n<2n_0+p+t$
証明書の $p$ をそのまま最小周期として出す証明書が最小周期とは限らない$p$ の約数で 1 周期を確かめて縮める
$h<n_0$ の山も周期で引く前周期を忘れる$h$ が計算済みなら $g(h)$ をそのまま使う

次のステップ

  • 発展1: 先頭 $B$ 個から算術周期性($g(n+p)=g(n)+c$)の証明書を探す版
  • 発展2: Q1(下限つきユークリッドゲーム)の鎖を「$k\in S$(一般の集合)」に広げ、本問の証明書で周期を確定させる
  • 発展3: 0.007(Treblecross)の $g$ を $n=2^{16}$ まで伸ばし、疎な値の手法と速さを比べる

検証ログ(本セッションで実行した数値)

  • 全探索(実測): 桁数 1〜4 の乱択コード、山 1〜3 個・大きさ 1〜14 の 1500 入力で、山の多重集合をメモ化した勝敗の全探索と、$g$ の XOR による判定が全件一致
  • 定理の確認(実測): 桁数 1〜4 の乱択コード 300 個のうち、$B\le4096$ で証明書が出たのは 146 個(前周期は最大 498、周期は最大 96)。その 146 個すべてで $g$ を $n<20000$ まで直接計算し、周期からの予測と全件一致
  • 誤った方法(実測、同じ 146 個): 先頭 200 個の後半だけで周期を決めると 14 個で値を誤る
  • 有名なゲーム(本セッションの計算値): 0.77 → $(12,71)$、0.07 → $(34,53)$、0.137 → $(34,52)$、0.4 → $(34,54)$、0.15 → $(10,1)$、0.35 → $(6,1)$。0.007・0.37・0.6 は $B=16384$ まで、0.16・0.106・0.777 は $B=4096$ まで証明書なし
  • 前周期の長い例: 桁数 5〜8 の乱択 200 コードで $n_0+p$ が最大だったのが 0.31511562($p=29,n_0=775$、$B=2048$ で証明書)
  • 時間(実測、起動込み、$N=2\times10^5$、$h_i\le10^{18}$): 0.31511562 0.07 s、0.77777777 0.06 s。証明書なしで $B=4096$ まで計算した 0.44444444 は 0.41 s、$B=16384$ の 0.007 は 0.98 s
  • 扱っていない範囲: 原典の確認、算術周期性、$d_0\ne0$ のゲーム、misère 版

自己評価

自分の回答

気づき・メモ