典型:オーバーキルDP

と名付けました

何?

「最大の個数/長さを求めよ」というときに、「長さ i にするために必要な最小コストは?」というDPをやって二部判定問題にするよ!

DPの遷移分の計算量を O(N) から O(log N) に減らすよ!

・LIS (最長増加列)

長さ N の数列 a[i] があるとき、単調増加な部分列で最長のものの長さをもとめる!

-- 愚直な実装

a[i] を一番最後とする列で最長のものを dp[i] とする。

遷移は a[j] < a[i] な jすべてについての a[j]+1 の max (なければ1) とする。

遷移に最悪 O(N) かかるので、O(N^2) のアルゴリズムである。

-- 高速化

dp[i][k] を「 i 番目までで長さ k の部分列を作るときの、最後の要素の最小値」とする。作れない場合は INF とする。そうすると、最後 dp[N][k] の中で INF でないような k の最大値が答えとなる。

遷移はどうなるか考える。dp[i-1][k] までは更新されてるとして、a[i] を使う部分列について考えると、 dp[i-1][k] の中で a[i] 以下の場所は変化せず、a[i] より大きい最小の k の場所だけ値が a[i] に更新される (INFの場所もそう)。

結局 i の情報は持つ必要がないので dp[k] のリストを更新する形となり、また dp[k] は単調増加なので遷移は二部探索が使えて O(log N) でできる。

「列を作るなら、ラストをなるだけ小さくしたい」という思想が大事。調べたい情報より強いことを求めるのでオーバーキルDPと呼ぶことにしました。