2021年7月24日土曜日

CodeChef - CCDSAP Exam

CodeChef - CCDSAP Exam

slope trickの練習問題として解いた。

iiの初期位置をaia_iとし、人iiを位置xxに配置した場合の人1,2,...,i1, 2, ..., iに関するコストの総和の最小値をfi(x)f_i(x)とする。

左右の境界を無視するとfi(x)=mintx2fi1(t)+xai=mintxfi1(t2)+xaif_{i}(x) = \min_{t \le x-2}f_{i-1}(t) + |x-a_{i}| = \min_{t \le x}f_{i-1}(t-2)+|x-a_i|と表せる。これだけなら普通のslope trickで解けるが実際には境界を越えられないところが難しい。

境界を越えたら大きなコストがかかるように、xai|x-a_i|の代わりに定数CCを定めてCmax(0,1x)+xai+Cmax(0,xN)C\max(0, 1-x) + |x-a_i| + C\max(0, x-N)を追加することを考えたが、これは優先度付きキューに一つずつ挿入していく方法では難しそう。平衡二分木で多重集合を管理すればいけそう? → 平衡二分木slope trickを作ろうとしたが、最小値の更新が自明にはできない。おそらく任意のxxについてf(x)f(x)の値が取得できる必要があって、実現方法がわからなかった。ただ、min\minがわからなくてもarg min\argminは容易に取得・更新できる。これだけわかれば最適解を貪欲に復元できそう。最適解における人iiの位置をyiy_iとする。yN=arg minxfN(x)y_N=\argmin_x f_{N}(x)は既知であり、yi+1y_{i+1}が既知であるときyiy_i

yi=min(arg minxfi(x),yi+12)y_i = \min(\argmin_x f_{i}(x), y_{i+1}-2)

と決定できる。arg min\argminが複数ある時はどれを取ってもよい。