F - Erase Subarrays Editorial by myau

ユーザ解説

公式解説とは異なる方法であり、本問題を解くにはやや煩雑ですが、汎用性のあるテクニックを紹介します。

\(dp[i][j]\) := 数列の\(i\)番目まで考えたとき、和を\(j\)にするために必要な操作回数の最小値
と定義します。

ある区間\([l+1,i]\)を残すとする\((0 ≦ l < i)\)と、
\(dp[i][j] = \min(dp[i][j], dp[l][j - (sum[i] - sum[l]) + 1)\)
と表すことができます。
\(sum[i], sum[l]\)などはもとの数列の累積和をとったものです

つまり、\(0 ≦ l < i\)に対して、\(dp[l][j - (sum[i] - sum[l])] \)の最小値が求まればよいことになります。
これを愚直に計算すると、この部分で\(O(N)\)の計算量を要してしまい、全体の時間計算量が\(O(N^2M)\)となりTLEしてしまいます。

これを高速化するにはどうすればよいでしょうか?
\(j - (sum[i] - sum[l]) = X\) とします。
求めたい値は、\(0 ≦ l < i\)に対する\(dp[l][j - (sum[i] - sum[l])] \) の最小値なので、\(dp[l][X]\)と書き直せます。
ここで式を移項して、\(j - sum[i] = -sum[l] + X\)としてみます。
\((i,j)\)を固定すると、右辺の値も求まるので、あらかじめ \(-sum[l] + X\) における\(dp[l][X]\)の最小値を計算しておくことで、\(O( \log N \max Ai)\)または\(O(1)\)で求めることができます(mapや配列を利用します)。

\(O(1)\)で計算した場合は全体の時間計算量が\(O(NM)\)になり、AC可能な解法となります。

実装例, C++, unordered_mapを使っています

累積和を用いたDP遷移をする際に、このような式変形をすることで遷移を高速化することができる場合があります。

posted:
last update: