問題
チョコレートの板が 3 行に並んでいる。1 行目(一番下)の長さが $a$、2 行目が $b$、3 行目が $c$ で $a\ge b\ge c\ge0$、$a\ge1$。1 行目の左端の 1 片は毒である。
2 人が交互に次の手を指す。
- 行 $r\in\{1,2,3\}$ と、その行の今の長さより短い $x\ge0$ を選び、行 $r$ とそれより上のすべての行の長さを $\min(\text{今の長さ},x)$ にする(選んだ片とその右上をすべて食べる)
- ただし 1 行目で $x=0$(毒を食べる)は選べない
手が無い(板が毒 1 片 $(1,0,0)$ だけ)プレイヤーの負けである。
$Q$ 個の局面 $(a,b,c)$ について、両者が最善を尽くしたとき先手が勝つなら First、後手が勝つなら Second を出力せよ。
入力形式
Q
a_1 b_1 c_1
...
a_Q b_Q c_Q
制約
| # | 制約 |
|---|---|
| 1 | $1\le Q\le2\times10^5$ |
| 2 | $0\le c_i\le b_i\le2000$ |
| 3 | $\max(b_i,1)\le a_i\le10^{18}$ |
入出力例
入力例1
6
2 1 0
3 1 1
4 1 1
7 4 3
8 4 4
1000000000000000000 3 1出力例1
Second
Second
First
Second
Second
First(1 つ目は 2 行の Chomp でよく知られた後手勝ち $a=b+1$。2 つ目 $(3,1,1)$ は $c>0$ なので 2 行の公式が使えない例で、$P(1,1)=3$。3 つ目は $a$ が 1 つずれるだけで先手勝ちになる。最後の $(b,c)=(3,1)$ には後手勝ちの $a$ が存在しない(1 行目を 2 に縮めた $(2,2,1)$ が後手勝ちなので、どんな $a$ でもそこへ動ける)。6 局面とも、全状態のミニマックス探索と一致)
背景と出典
- Chomp は Gale(1974)が名付け、それ以前に Schuh(1952)が約数のゲームとして考えた、と私は記憶している(原典は本セッションで未確認)。長方形の板では先手必勝であることが戦略盗み論法で示せるが、勝ち手を作る一般の方法は知られていない
- 3 行の Chomp は Zeilberger の “Three-Rowed CHOMP”(Advances in Applied Mathematics, 2001 と記憶、未確認)がコンピュータで調べた対象で、「$(b,c)$ を固定すると後手勝ちの $a$ は高々 1 つ」という見方もそこで使われていたと記憶している。本問の問題文・制約・入力例は自作で、性質は本セッションの全探索で確かめた
- Day161〜176 の不偏ゲームは Grundy 数で和を扱ったが、本問は 1 局面の勝敗だけを問う。Grundy 数を全状態で求める道は $O(N^3)$ 状態が要り、$N=2000$ では 13 億状態で届かない
概念図
ヒント(クリックで段階的に開示)
ヒント1(方向性)
局面 $(a,b,c)$ から「1 行目だけを $x$($b\le x<a$)に縮める」手を指すと $(x,b,c)$ になり、$b,c$ は変わらない。$(a,b,c)$ と $(a',b,c)$($a<a'$)が両方とも後手勝ちになることはありうるか。
ヒント2(アプローチ)
ありえない(大きい方から小さい方へ 1 手で行けるから)。よって $(b,c)$ ごとに後手勝ちの $a$ は高々 1 つで、それを $P(b,c)$(無ければ「無し」)と書く。$(a,b,c)$ からの手を 3 種類に分ける。
- 1 行目を $x<b$ に: $(x,x,\min(c,x))$。$a$ によらない
- 2 行目を $y<b$ に: $(a,y,\min(c,y))$。後手勝ち $\iff a=P(y,\min(c,y))$
- 3 行目を $z<c$ に: $(a,b,z)$。後手勝ち $\iff a=P(b,z)$
ヒント3(誘導)
- 1 行目を $x<b$ に縮めた $(x,x,\min(c,x))$ のどれかが後手勝ちなら、$P(b,c)$ は無し
- そうでなければ $P(b,c)$ は「$\max(b,1)$ 以上で、集合 $\{P(y,\min(c,y))\}_{y<b}\cup\{P(b,z)\}_{z<c}$ に入らない最小の $a$」(mex)
- $b$ の小さい順、同じ $b$ では $c$ の小さい順に決める。集合を Python の整数のビットで持てば、mex は「下位から最初の 0 ビット」で求まる
模範解答 (Python)
模範解答を表示する(まず自力で解いてから)
import sys
def main():
data = sys.stdin.buffer.read().split()
q = int(data[0])
qs = [(int(data[1 + 3 * i]), int(data[2 + 3 * i]), int(data[3 + 3 * i])) for i in range(q)]
n = max(b for _, b, _ in qs)
NONE = -1
# P[b][c]: (a,b,c) が後手勝ち(P 局面)になる唯一の a。無ければ NONE
P = [[NONE] * (b + 1) for b in range(n + 1)]
# rowmask[c]: y<b についての P(y, min(c,y)) をビットで持つ(2 行目を y に縮める手の行き先)
rowmask = [0] * (n + 1)
# dead[c]: 1 行目を x (1<=x<b) に縮めた (x,x,min(c,x)) が P になる x があるか
dead = [False] * (n + 1)
for b in range(n + 1):
colmask = 0 # z<c についての P(b,z)(3 行目を z に縮める手の行き先)
Pb = P[b]
lo = b if b >= 1 else 1
for c in range(b + 1):
if dead[c]:
v = NONE
else:
x = (rowmask[c] | colmask) >> lo
v = lo + (~x & (x + 1)).bit_length() - 1 # lo 以上で、どちらの集合にも無い最小の a
colmask |= 1 << v
Pb[c] = v
for c in range(n + 1):
v = Pb[c if c < b else b]
if v >= 0:
rowmask[c] |= 1 << v
if b >= 1 and v == b: # (b,b,min(c,b)) が P → 以後この c では 1 行目を b に縮めて勝てる
dead[c] = True
out = []
for a, b, c in qs:
out.append("Second" if P[b][c] == a else "First")
print("\n".join(out))
main()
計算量: $P$ の表は $O(N^2)$ 個($N=\max b$)。各値の mex は長さ $O(N)$ ビットの整数演算 1 回なので全体 $O(N^3/w)$($w$ は機械語の幅)。$N=2000,Q=2\times10^5$ で 0.77 s・152 MB、$N=3000$ で 1.84 s・226 MB(Python 3.14.8、Apple M5 Pro、負荷平均 4〜13 での実測、起動込み)。
Step-by-Step 解説
11 行目だけを縮める手が「一意性」を生む
$(a',b,c)$ から 1 行目を $a$($b\le a<a'$)に縮めると $(a,b,c)$ になる。もし両方が後手勝ちなら、後手勝ちの局面から後手勝ちの局面へ 1 手で行けることになり矛盾。よって $(b,c)$ ごとに後手勝ちの $a$ は高々 1 つ。全探索($a\le60,b\le24$ の 14624 局面)でも、どの $(b,c)$ でも後手勝ちは 1 つ以下だった。
2手を 3 種類に分け、$a$ への依存を見る
- 1 行目を $x\ge b$ に: $(x,b,c)$。$x<a$ の全部が「先手勝ち」であることが必要 → $a$ を最小に選べば自動で満たされる($x<a$ はどれも、下の集合に入っていて後手勝ちへ動ける)
- 1 行目を $1\le x<b$ に: $(x,x,\min(c,x))$。$a$ によらないので、1 つでも後手勝ちなら $(b,c)$ の全局面が先手勝ち($P(b,c)$ は無し)。これを
dead[c]で持つ - 2 行目・3 行目を縮める手: 行き先が後手勝ちになる $a$ はそれぞれ 1 つだけ($P$ の値)。これらを避ける
3mex で $P(b,c)$ を決める
$$P(b,c)=\operatorname{mex}_{\ge\max(b,1)}\Bigl(\{P(y,\min(c,y))\}_{0\le y<b}\ \cup\ \{P(b,z)\}_{0\le z<c}\Bigr)$$
(dead[c] なら無し)。始まりは $P(0,0)=1$(毒 1 片は手が無いので後手勝ち)。$P(1,0)=2$ は 2 行の公式 $a=b+1$ と一致する。
4集合をビットで持つ
2 行目の手の集合は $c$ ごとに rowmask[c] として、$b$ が 1 増えるたびに $P(b,\min(c,b))$ を足す。3 行目の手の集合は同じ $b$ の中で colmask に足していく。mex は x = (rowmask[c] | colmask) >> lo の最下位の 0 ビットで、(~x & (x+1)).bit_length() - 1。集合(set)で 1 つずつ数える版は $N=2000$ で 15.20 s、ビット版は 0.61 s(どちらも表作りだけ、実測)。
5質問に答える
表ができれば $(a,b,c)$ は「$a=P(b,c)$ なら後手勝ち」。$a$ が $10^{18}$ でも比べるだけ。$N=2000$ での $P$ の最大値は 3416($\approx1.71N$)、表の 2003001 個のうち 827587 個(41%)が「無し」だった。
迷った他案と採らなかった理由
| 案 | 計算量 | 採否 | 理由 |
|---|---|---|---|
| 全局面のミニマックス(メモ化) | $O(A\cdot N^2\cdot(A+N))$ | 検算用のみ | $a$ が $10^{18}$ まであり、$a$ を状態に持てない |
| 全局面の Grundy 数 | 同上 | 不採用 | 和を問わないので勝敗だけで足りる。Grundy 数では一意性が使えない |
| 2 行の公式 $a=b+1$ を流用 | $O(1)$ | 不採用(誤り) | $c>0$ で崩れる。全探索 14624 局面中 490 で誤答(実測) |
「1 行目を $x<b$ に縮める手」を見落とす(dead なし) | 同じ | 不採用(誤り) | 454 局面で誤答(実測)。$(b,c)=(3,1)$ など「後手勝ちが無い」組を作ってしまう |
| mex を 3 行目の集合だけ/2 行目の集合だけで取る | 同じ | 不採用(誤り) | それぞれ 260・490 局面で誤答(実測) |
mex を set で 1 つずつ数える | $O(N^3)$ | 不採用(遅い) | $N=1000$ で 1.32 s、$N=2000$ で 15.20 s(表作りだけ、実測) |
| mex をビット演算で(本解) | $O(N^3/w)$ | 採用 | $N=2000$ で表作り 0.61 s |
前提が変わったら
- $b$ が $10^4$ 以上なら $O(N^2)$ 個の表そのもの($5\times10^7$ 個)がメモリに載らない。表を作らず、質問の $(b,c)$ に必要な行だけを作る工夫が要る(本セッションでは試していない)
- 4 行以上なら「$(b,c,d)$ ごとに $a$ が 1 つ」は同じ論法で成り立つが、表が $O(N^3)$ になる
- 和(複数の板)を問われたら、勝敗だけでは足りず Grundy 数が要り、一意性による圧縮が使えない。この場合は $a$ も小さく制限するしかない
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| 2 行の公式 $a=b+1$ を 3 行でも使う | 小さい例からの一般化 | $c>0$ で崩れる(入力例 2 の $(3,1,1)$) |
| 「後手勝ちの $a$ は必ずある」と思う | mex は必ず値を返すので | 1 行目を $x<b$ に縮めた先が後手勝ちなら無し(入力例 6) |
| mex の下限を $b$ にして $b=0$ で $a=0$ を返す | 毒の片を忘れる | 下限は $\max(b,1)$ |
| 3 行目を縮める手の集合を $c$ をまたいで共有しない | 内側のループで初期化し忘れ/しすぎ | 同じ $b$ の中で $c$ を増やしながら足す |
rowmask に $P(b,c)$ を足す($\min(c,b)$ でなく) | 2 行目を $b$ に縮めると 3 行目も $\min(c,b)$ になることを忘れる | 3 行目は 2 行目より長くなれない |
次のステップ
- 発展1: 4 行の Chomp で同じ表を作り、$b,c,d\le60$ でどこまで計算できるか測る
- 発展2: 「無し」になる $(b,c)$ の割合($N=2000$ で 41%)が $N$ とともにどう変わるかを調べる
- 発展3: 2 枚の 3 行 Chomp の和を Grundy 数で解き、一意性が使えなくなる理由を確かめる
検証ログ(本セッションで実行した数値)
- 全探索との照合(実測): $1\le a\le60$、$0\le c\le b\le\min(a,24)$ の 14624 局面をメモ化ミニマックスで解き、本解と全件一致。後手勝ちは 215 局面で、どの $(b,c)$ でも 1 つ以下
- 誤った方法(実測、同じ 14624 局面): 2 行の公式 490、
deadなし 454、3 行目の集合なし 490、2 行目の集合なし 260 で誤答 - 表の作成時間(実測、表作りだけ):
set版 $N=500/1000/1500/2000$ で 0.17/1.32/5.73/15.20 s、ビット版 0.03/0.12/0.33/0.61 s - 全体(実測、起動込み、$Q=2\times10^5$、$a$ は $10^{18}$ まで乱数): $N=2000$ で 0.77 s・152 MB、$N=3000$ で 1.84 s・226 MB
- $N=2000$ の表: $P$ の最大 3416、「無し」は 2003001 個中 827587 個
- 扱っていない範囲: Zeilberger の論文の内容確認、4 行以上、和のゲーム