問題
オクタルゲーム $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 も同じ)
- 本問の問題文・制約・入力例は自作
概念図
ヒント(クリックで段階的に開示)
ヒント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–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 |
$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 版