Day 178-Q2 — 2026-10-14 | 4 行の Chomp((b,c,d) ごとに後手勝ちの a は高々 1 つ。P(b,c,d) を 3 種の「縮める手」の集合の mex で O(N^3) 個の表に)

2026-10-14 赤色 Master (Phase 8+) ★★★★★★★★★ ゲーム理論(焦点帯 M1)。Day177-Q1(3 行の Chomp)の発展「4 行の P 表と『無し』の割合」。3 行で使った「上の行を固定すると後手勝ちの 1 行目の長さは高々 1 つ」は 4 行でもそのまま成り立つ。行き先の集合が 2 種から 3 種(2・3・4 行目を縮める手)に増え、表が $O(N^3)$ になる

問題

チョコレートの板が 4 行に並んでいる。1 行目(一番下)の長さが $a$、2 行目が $b$、3 行目が $c$、4 行目が $d$ で、$a\ge b\ge c\ge d\ge0$、$a\ge1$。1 行目の左端の 1 片は毒である。

2 人が交互に次の手を指す。

  • 行 $r\in\{1,2,3,4\}$ と、その行の今の長さより短い $x\ge0$ を選び、行 $r$ とそれより上のすべての行の長さを $\min(\text{今の長さ},x)$ にする(選んだ片とその右上をすべて食べる)
  • ただし 1 行目で $x=0$(毒を食べる)は選べない

手が無い(板が毒 1 片 $(1,0,0,0)$ だけ)プレイヤーの負けである。

$Q$ 個の局面 $(a,b,c,d)$ について、両者が最善を尽くしたとき先手が勝つなら First、後手が勝つなら Second を出力せよ。

入力形式

Q
a_1 b_1 c_1 d_1
...
a_Q b_Q c_Q d_Q

制約

#制約
1$1\le Q\le2\times10^5$
2$0\le d_i\le c_i\le b_i\le200$
3$\max(b_i,1)\le a_i\le10^{18}$

入出力例

入力例1

6
4 1 1 1
3 1 1 1
3 1 1 0
5 2 1 1
8 3 3 3
1000000000000000000 4 1 1

出力例1

Second
First
Second
Second
Second
First

(2 つ目と 3 つ目が要点。3 行の Chomp で後手勝ちの $(3,1,1)$ に 4 行目を 1 片足すと先手勝ちになり、後手勝ちの 1 行目は 4 に移る。最後の $(b,c,d)=(4,1,1)$ には後手勝ちの $a$ が存在しない(1 行目を 3 に縮めた $(3,3,1,1)$ が後手勝ちなので、どんな $a$ でもそこへ動ける)。6 局面とも全状態のミニマックス探索と一致)

背景と出典

  • Chomp は Gale(1974)が名付け、Schuh(1952)が約数のゲームとして考えた、と私は記憶している(原典は未確認)。長方形の板では戦略盗み論法で先手必勝だが、勝ち手を作る一般の方法は知られていない
  • 3 行の Chomp は Zeilberger “Three-Rowed CHOMP”(2001 と記憶、未確認)がコンピュータで調べた。4 行以上を扱った文献があるかは本セッションで調べていない
  • 本問の問題文・制約・入力例は自作。性質「$(b,c,d)$ ごとに後手勝ちの $a$ は高々 1 つ」と表の作り方は本セッションで導き、全探索で確かめた

概念図

局面 (a,b,c,d) = (6, 3, 2, 1):下が 1 行目、左下の × が毒 a=6 b=3 c=2 d=1 × 2 行目を y に → (a, y, min(c,y), min(d,y)) 集合 R:P(y, min(c,y), min(d,y)) 3 行目を z に → (a, b, z, min(d,z)) 集合 C:P(b, z, min(d,z)) 4 行目を w に → (a, b, c, w) 集合 W:P(b, c, w) 1 行目を x < b に → (x, x, ·, ·) P(x,…) = x なら「無し」 1 行目を x ≥ b に縮めても上の行は変わらない → (b,c,d) ごとに後手勝ちの a は高々 1 つ = P(b,c,d) P(b,c,d) = R ∪ C ∪ W に無い max(b,1) 以上の最小の数(mex)。b, c, d の昇順に埋めれば 3 つの集合はすべて計算済み 実測:b ≤ 12, a ≤ 50 の 19109 局面で全探索と一致。N = 200 で 44.2% が「無し」、P の最大 459

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

ヒント1(方向性)

$(b,c,d)$ を固定する。後手勝ち(P 局面)の $a$ が 2 つ $a<a'$ あったら、$(a',b,c,d)$ から 1 行目を $a$ に縮める手($a\ge b$ なので上の行は変わらない)で P へ動けてしまう。だから P の $a$ は高々 1 つ。これを $P(b,c,d)$ と書く(無ければ「無し」)。

ヒント2(アプローチ)

$(a,b,c,d)$ から 1 行目以外を縮める手は 3 種:

  • 2 行目を $y<b$ に: $(a,\ y,\ \min(c,y),\ \min(d,y))$。P なのは $a=P(y,\min(c,y),\min(d,y))$ のとき
  • 3 行目を $z<c$ に: $(a,b,z,\min(d,z))$。P なのは $a=P(b,z,\min(d,z))$
  • 4 行目を $w<d$ に: $(a,b,c,w)$。P なのは $a=P(b,c,w)$

1 行目を $x\in[b,a)$ に縮める手は「より小さい $a$ の P」へ行く手。1 行目を $x<b$ に縮める手は $a$ によらない行き先 $(x,x,\min(c,x),\min(d,x))$。

ヒント3(誘導)
  • 1 行目を $x$($1\le x<b$)に縮めた局面が P になる $x$ があれば、$P(b,c,d)=$ 無し(どの $a$ でもそこへ勝ちに行ける)。それは $P(x,\min(c,x),\min(d,x))=x$ のこと
  • 無しでなければ、$P(b,c,d)$ は「3 種の集合のどれにも無い、$\max(b,1)$ 以上の最小の数」= mex
  • 集合を整数のビットで持つ。$b$ の昇順、$c$ の昇順、$d$ の昇順に埋めると、3 つの集合はどれも計算済みの値だけでできる

模範解答 (Python)

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


def build(n):
    """P[b][c][d]: (a,b,c,d) が後手勝ちになる唯一の a(無ければ -1)。n >= b >= c >= d >= 0"""
    NONE = -1
    P = [[[NONE] * (c + 1) for c in range(b + 1)] for b in range(n + 1)]
    # rowmask[c][d]: b'<b の P(b', min(c,b'), min(d,b'))(2 行目を縮める手の行き先)
    rowmask = [[0] * (n + 1) for _ in range(n + 1)]
    # dead[c][d]: 1 行目を x (1<=x<b) に縮めた (x,x,min(c,x),min(d,x)) が後手勝ちになる x があるか
    dead = [[False] * (n + 1) for _ in range(n + 1)]
    for b in range(n + 1):
        lo = b if b >= 1 else 1
        Pb = P[b]
        colmask = [0] * (n + 1)           # z<c の P(b, z, min(d,z))(3 行目を縮める手)
        for c in range(b + 1):
            Pbc = Pb[c]
            rm = rowmask[c]
            dd = dead[c]
            cm = colmask
            wmask = 0                     # w<d の P(b, c, w)(4 行目を縮める手)
            for d in range(c + 1):
                if dd[d]:
                    v = NONE
                else:
                    x = (rm[d] | cm[d] | wmask) >> lo
                    v = lo + (~x & (x + 1)).bit_length() - 1
                    wmask |= 1 << v
                Pbc[d] = v
            for d in range(n + 1):        # 後の c' > c から見た「3 行目を c に縮める手」
                v = Pbc[d if d < c else c]
                if v >= 0:
                    colmask[d] |= 1 << v
        for c in range(n + 1):            # 後の b' > b から見た「2 行目を b に縮める手」
            cc = c if c < b else b
            Pbc = Pb[cc]
            rm = rowmask[c]
            dd = dead[c]
            for d in range(c + 1):
                v = Pbc[d if d < cc else cc]
                if v >= 0:
                    rm[d] |= 1 << v
                    if b >= 1 and v == b:  # (b,b,..) が後手勝ち → 以後この (c,d) は 1 行目を b に縮めて勝てる
                        dd[d] = True
    return P


def main():
    data = sys.stdin.buffer.read().split()
    q = int(data[0])
    qs = [tuple(int(v) for v in data[1 + 4 * i:5 + 4 * i]) for i in range(q)]
    n = max(b for _, b, _, _ in qs)
    P = build(n)
    print("\n".join("Second" if P[b][c][d] == a else "First" for a, b, c, d in qs))


if __name__ == "__main__":
    main()

計算量: 表は $(b,c,d)$ の $O(N^3/6)$ 個、各 mex は長さ $O(N)$ ビットの整数演算なので全体 $O(N^4/w)$。$Q=2\times10^5$ 込みで $N=100$: 0.23 s・86 MB、$N=200$: 0.79 s・113 MB、$N=300$: 2.71 s・210 MB、$N=400$: 6.65 s・401 MB(Python 3.14.8、Apple M5 Pro、負荷平均 20〜38 での実測、起動込み)。これを見て制約を $b\le200$ とした。

Step-by-Step 解説

11 行目だけが特別

1 行目を $x\ge b$ に縮めても上の行は変わらない。だから $(b,c,d)$ を固定した「1 行目の長さ $a$」の列は、Nim の 1 山のように「より小さい $a$ へ動ける」。後手勝ちは高々 1 つ(ヒント 1)。

2行き先の 3 つの集合

2・3・4 行目を縮める手の行き先は、どれも 1 行目が $a$ のまま。行き先が P になる $a$ の集合を

  • $R(c,d)=\{P(y,\min(c,y),\min(d,y)) : y<b\}$
  • $C(d)=\{P(b,z,\min(d,z)) : z<c\}$
  • $W=\{P(b,c,w) : w<d\}$

とする。$a$ がこれらに入っていれば、その手で後手勝ちへ動けるので先手勝ち。

3mex と「無し」

$a\ge\max(b,1)$ で、3 つの集合に入らず、かつそれより小さい P の $a$ も無い(それは自動的に満たす。最小を取るので)なら P。ただし 1 行目を $x<b$ に縮めた局面 $(x,x,\cdot,\cdot)$ が P なら、どの $a$ でも勝てるので無し。その判定は $P(x,\min(c,x),\min(d,x))=x$。

4埋める順序

$b$ の昇順に進めれば $R$ は「前の $b$ までの値」、$c$ の昇順に進めれば $C$ は「同じ $b$ の前の $c$ の値」、$d$ の昇順に進めれば $W$ は「同じ $(b,c)$ の前の $d$ の値」だけでできる。3 行の Day177-Q1 の rowmask・colmask に、4 行目用の wmask が 1 つ加わった形。

5mex をビット演算で

集合を整数のビット列で持ち、x = mask >> lo の最下位の 0 ビットを (~x & (x+1)).bit_length()-1 で取る。Day177-Q1 で set 版と比べて 25 倍速かった方法をそのまま使う。

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

案計算量採否理由
全局面のミニマックス局面 $O(AN^3)$検算用のみ$a\le10^{18}$ では不可能
3 行の表を使い 4 行目を無視$O(N^3/w)$不採用(誤り)$b\le12,a\le50$ の 19109 局面中 544 で誤る(実測)
「無し」の判定を省く$O(N^4/w)$不採用(誤り)676/19109 誤り(実測)
4 行目を縮める手の集合 $W$ を省く同上不採用(誤り)544/19109 誤り(実測)
3 行目を縮める手の集合 $C$ を省く同上不採用(誤り)543/19109 誤り(実測)
Grundy 数の表を全局面で作る$O(N^4)$ 局面 × $a$不採用勝敗だけなら P の $a$ 1 つで足りる
$(b,c,d)$ ごとの P を mex で(本解)$O(N^4/w)$採用$N=200$ で 0.79 s

前提が変わったら

  • $k$ 行に一般化すると、表は $O(N^{k-1})$ 個、行き先の集合は $k-1$ 種になる。5 行なら $N=60$ 程度が Python の目安($N^4/24\approx5\times10^5$ 個)と見積もるが、測っていない
  • $b$ が $10^3$ 以上なら $O(N^3)$ 個の表がメモリに載らない($N=400$ で 401 MB)。$a$ の上限が無い問いに答えるには表が要るので、局面ごとに必要な $(b,c,d)$ だけを遅延で作る工夫が要る(未実装)
  • 勝ち手を 1 つ出せなら、どの集合($R,C,W$)に $a$ が入ったかを覚えておけば復元できる

よくあるミス

ミス原因正しい書き方
2 行目を $y$ に縮めたとき 3・4 行目を $\min$ しない「その行だけ短くなる」と読む上の行すべてを $\min(\cdot,y)$
mex の下限を $b$ にし忘れる$a\ge b$ の制約を忘れる$\max(b,1)$ 以上の最小
$b=0$ のとき下限を 0 にする毒 1 片 $(1,0,0,0)$ の扱い$a\ge1$ なので下限 1
「無し」を $x\in[0,b)$ で判定1 行目 $x=0$ は毒で選べない$1\le x<b$
colmask を $b$ ごとに作り直さない3 行目を縮める手は同じ $b$ の中だけ$b$ ごとに 0 から

次のステップ

  • 発展1: 5 行の表を作り、「無し」の割合が 3 行 41%・4 行 44% からどう変わるか測る
  • 発展2: $P(b,c,d)$ の最大値が $N=200$ で 459、$N=400$ で 914(どちらも約 $2.3N$)だった。3 行は $N=2000$ で 3416(約 $1.7N$)。行数と係数の関係を調べる
  • 発展3: 長方形の板($a=b=c=d$)の先手の勝ち手を表から求め、戦略盗み論法の「存在」を具体的な手にする

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

  • 正しさ(実測): $b\le12$、$\max(b,1)\le a\le50$ の全 19109 局面で、全状態のミニマックス探索と表の判定が全件一致($b\le12$ の $P$ の最大は 28 なので、$a\le50$ ですべての P を含む)。$b\le9,a\le30$ の 5334 局面でも一致
  • 誤った方法(実測、同じ 19109 局面): 3 行の表 544、「無し」を省く 676、$W$ を省く 544、$C$ を省く 543 局面で誤る
  • 表の性質(実測): $N=50$ で 23426 個中 10308 個(44.0%)、$N=100$ で 44.8%、$N=200$ で 1373701 個中 607792 個(44.2%)、$N=400$ で 43.3% が「無し」。$P$ の最大は $N=50$: 115、$N=100$: 229、$N=200$: 459、$N=400$: 914
  • 具体例(実測): $P(1,1,1)=4$、$P(2,1,1)=5$、$P(1,1,0)=3$(3 行の $(3,1,1)$)、$P(3,3,3)=8$、$P(4,1,1)=$ 無し
  • 時間(実測、起動込み、$Q=2\times10^5$、負荷平均 20〜38): $N=100$ 0.23 s、$N=200$ 0.79 s・113 MB、$N=300$ 2.71 s・210 MB、$N=400$ 6.65 s・401 MB
  • 扱っていない範囲: 4 行以上の文献、5 行以上、勝ち手の復元

自己評価

自分の回答

気づき・メモ