バイトの競プロメモ

主に競技プログラミング

F - Insertion Sort - マイナビプログラミングコンテスト2021(AtCoder Beginner Contest 201)

セグ木で差分を更新する方法が勉強になった。

F - Insertion Sort

----------------------------------------------------

問題

長さNの順列Pが与えられる。

左からi番目の値はPiである。

以下の操作を使ってPiを昇順に並べたい。

・i(1<=1<=N)を選ぶ。コストAiでiを好きな場所に移動

・i(1<=1<=N)を選ぶ。コストBiでiを左端に移動

・i(1<=1<=N)を選ぶ。コストCiでiを右端に移動

合計コストを最小化してください。

-----------------------------------------------------

動かさない番号をS1....Skとすると

S1未満の番号は左端に動かすべきであり

Skより上の番号は右端に動かすべきであり

それ以外は任意の場所に動かす必要があると決まる。

 

よって以下のようにdpを決める。

dp[i] := i番目が動かさない最後の番号とした時に、i番目までを昇順に並べる最小コスト

 

ここで答えはmin(dp[i] + (A[i+1] + ...  + A[N-1]));になる

またdpの遷移は以下のように書ける。

dp[i] = min(B[0]+...+B[i-1], dp[j] + A[j+1]+...+A[i-1]);

左は自分の左に動かさない番号が無い場合であり

右は直前の動かない番号がjである場合である。

 

この遷移をセグ木で高速化する。

厄介なのは選んだjによって足されるAの値が異なる事であり、セグ木から取り出した値に同じ値を足せば

dpの値になるように出来ればいいので

 

seg[i] := dp[i] - A[i]

としておけば

任意のjについてdp[i] = seg(0, i) + A[0]+...+A[i-1]

と扱えるためこの問題が解けた

 

図にすると下のようになる

 

答えの値について緑の長さを足してやる必要がある時に

あらかじめ赤線の部分を引いておくことで

どの値についても同じ緑の長さを足せばいいようにした。

f:id:baitop:20220209221948p:plain