Official

G - Count Grid 3-coloring Editorial by toam


行と列を反転しても答えは変わらないため,\(W\leq H\) を仮定します.

左上のマスから右下のマスにかけて,\((1,1)\to(1,2)\to\dots\to(1,W)\to(2,1)\to(2,2)\to\dots\to(2,H)\to\dots\to(H,1)\to(H,2)\to\dots\to(H,W)\) の順に数字を決めていきながら答えを dp で求めることにします.

上の図で緑色のマスの色の数字に注目している場合を考えます.このとき,上側にある灰色のマスの情報はその後の数字の決め方に一切関与しません.青色のマスの数字の書かれ方のみによって,それ以降の数字の決め方が変わってきます.よって,「最後に見た \(W\) 個のマスの数字の書き方」を key として dp ができそうです.

状態数について考えます.「最後に見た \(W\) 個のマスの数字の書き方」は最大で \(3^W\) 通りあり多いですが,そのうち「隣接した数字が異なる」という条件を満たすものは高々 \(9\times 2^{W-2}\) 通りしかありません.よって,条件を満たすものだけを状態として持つことで状態数を削減することができます.

計算量は \(O(2^{\min(H,W)}HW)\)\(O(2^{\min(H,W)}HW\min(H,W))\) となり,\(\min(H,W)\leq 14\) であることを踏まえると間に合います.

類題

from collections import defaultdict

H, W = map(int, input().split())
S = [list(input()) for i in range(H)]
if W > H:
    S = [[S[i][j] for i in range(H)] for j in range(W)]
    H, W = W, H

mod = 998244353

# 前 W マスの状態を W 桁の 10 進法として持つ
dp = defaultdict(int)
dp[0] = 1
B = 10 ** (W - 1)
for x in range(H):
    for y in range(W):
        ndp = defaultdict(int)
        for pre, val in dp.items():
            for i in range(1, 4):
                if S[x][y] == str(i) or S[x][y] == "?":
                    top = pre // B
                    if top == i:  # 真上のマスと同じ場合
                        continue
                    if y > 0:
                        back = pre % 10
                        if back == i:  # 左のマスと同じ場合
                            continue
                    nxt = (pre * 10 + i) % (10 * B)
                    ndp[nxt] += val
                    ndp[nxt] %= mod
        dp = ndp

ans = sum(dp.values()) % mod
print(ans)

posted:
last update: