Official

B - JANKEN Machine Editorial by enjapma


まず、 \(R, P, S\)\(0, 1, 2\) に変換します。

そして、長さ \(N\) のジャンケン文字列 \(S\) を、以下の条件を満たす長さ \(N-1\) の数列 \(S'\) で表します。

  • $1 \le i \le N-1$ に対して $S'_i \in \{-1, 0, 1\}$
  • $S_i + S'_i = S_{i+1}$

\(S\) をジャンケンマシーンに入れて得られる文字列を \(T\) として、\(T'\) も同様に定義します。\(S'\)\(T'\) を比較して観察すると、以下の重要な事実を得ます。

[事実] \(S'\) に現れる全ての \(-1\) をそれぞれ \(1\) つ左に移動させ(このとき、\(1\) と場所が重なったものについては \(0\) に置き換える)、一番右の文字を削除することで \(T'\) が得られる。

さて、\(k\) 回の操作をまとめて逆向きに行うことを考えましょう。\(-1\) の要素を全て \(k\) ずつ右に移動させます。この際に既存の \(1\) と交差するようなことがあれば、問題の答えは \(0\) です。

あとは、\(-1\)\(1\) の間の各区間について、「\(k\) 回の操作をした後、全ての \(1\)\(-1\) が打ち消し合うように数字を埋める場合の数」を求め、それらを掛け合わせれば良いです。

この場合の数は、以下のようなDPで計算できます。

\(dp[N]\) を、「長さ \(N\) の区間についての答え」と定義すると、区間の一番左に \(0\) を書き込む場合と \(1\) を書き込む場合に場合分けをすると、以下のような漸化式が成り立ちます。

// 一番左に 0 を書き込んだ場合
dp[i] += dp[i - 1];

// 一番左に 1 を書き込んだ場合
for (int j = 0; j <= min(i - 2, k - 1); j++) {
   dp[i] += dp[j] * dp[i - j - 2];
}

なお、数列の両端の部分や、\(S'\)\(1\) 種類の文字からなる場合には少し違う漸化式になり、例外処理が必要です。

bonus : \(N, k \le 10^5\) の場合の解法も考えてみてください。

posted:
last update: