Q - 連結 Editorial by Kiri8128


【考え方】

連結のしかたが異なっても文字列として同じであれば同じとみなすというところが厄介です。 ここでは文字列を主体としてそれが達成可能かどうかという考え方が良さそうです。 より細かく、ある文字列について、直近の \(x\) 文字前で区切ったものが達成できるかどうかで分けると良いです(\(x=0\) で達成できるものが求めるものです)。 \(x\)\(0 \le x \le 7\) を考えれば十分です。

【解法】

\({\rm DP}[n][i][j]\): 長さ \(n\) の文字列で、直近の \(7\) 文字のビットが \(i\ (0\le i \le 127)\) かつ直近の区切りとしてありえるもののビットが \(j\ (0\le j \le 255)\) であるものの個数

とします。 (この \(j\) は上の条件を満たす \(x\) について \(2^x\) を合計したものと一致します。なお \(n\) を固定すると、 \({\rm DP}[n][i][j]\) の合計は長さ \(n\) の文字列の個数すなわち \(2^n\) と一致します。)

あとは \((i, j) \rightarrow (i', j')\) の遷移を考えれば良いです。 最後に付けた文字を \(k\ (k=0, 1)\) とすると、 \(i' = i * 2 + k \bmod 128\) です。 \(j'\)\(j\)\(2\) 倍(\(\bmod 256\))またはそれに \(1\) を足したものです。 \(1\) を足す条件は、直近の区切りいずれかからの文字列を持っていることです。 これは各 \(i\) について可能な位置を表すビット列を前計算しておくことで、 \(O(1)\) 回のビット演算で計算できます。

よって配る DP の要領で更新すると各 \(n, i, j\) について配り先が \(O(1)\) で分かるので、 \(M = \max(|w_i|)\) とすると全体の計算量は \(O(L \times 2^{2M})\) になります。これは問題の制約下で十分高速です。

AC コード(PyPy3)

posted:
last update: