olpheの競プロ帖

競プロ問やアルゴリズム等の考察します

全国統一プログラミング王決定戦本戦-F Flights

問題概要

N個の頂点があり、各頂点はx,y座標とコストを持っている。これらをx,y,costで表す。頂点i,jを考えたときに、x_i \leqq x_j , y_i \leqq y_jを満たすときにij間に距離cost_jの辺が貼られる。

スタートからゴールまでの距離の最小値を求めたい。距離はdistで表す。

 

メモ

まずスタートとゴールを(x,y)で比較したときに、常にスタートが小さくなるようスタートをゴールを入れ替えても一般性を失わない。

(x,y)の昇順に頂点を見ていくことで、移動を「左下からの移動」「左下への移動」の2種類のみとして考えることができる。

 

左下からの移動

頂点iに左下にある頂点kから移動する場合

 dist_i=min(dist_k)+cost_iである。

 

これは各頂点に対してO(N)であるため後に高速化することを考える。

 

左下への移動

頂点iから左下にある頂点kへ移動する場合、全てのkに対して 

 dist_k=min(dist_k,dist_i+cost_i)である。

 

これも各頂点に対してO(N)であるため後に高速化することを考える。

 

またこの時ゴールの右上にあるすべての頂点kも考えて、

ans=min(dist_i,min(dist_k+cost_k))である。

 

mapを用いた高速化

(y,x)をキーにしてdistを持つmapを用いることで、2種類の移動の際に見るべき頂点の数を1つにすることができる。これを実現するためにはmapの値が常に単調減少であるようにすれば良い。

頂点iを見ているときのことを考える。

左下からの移動

mapの値が単調減少であれば、(y_i,x_i)よりも小さい要素をキーに持つ要素の中でキーが一番大きな要素のみを見たら良い。これはlower_boundとprevを用いることなどで値の更新ができる。もしそのような要素が無ければその頂点にたどり着くことは不可能である。

但し追加の過程で単調減少でなくなる場合があるので、その場合mapから頂点iを削除するか、頂点iよりも後の要素の値が頂点iでの値よりも小さくなるまで頂点iの直後の要素を削除し続けることで単調減少を維持することができる。

左下への移動

 mapの先頭から見ていって値がdist_i+cost_iよりも大きい要素をdist_i+cost_iに変更することで値の更新ができる。ここでも単調減少でなる場合があるので、mapの先頭の値よりも2番目の値が小さくなるまで2番目の要素を削除し続けることで単調減少を維持することができる。

 

 

実際にACした提出

atcoder.jp

遅延セグ木を用いた高速化

座標圧縮後の各y座標以上に到達するための最小距離の最小値を持ったセグ木を用いる。セグ木はsegで表す。

RMQ(a,b)は[a,b]の最小値を求める関数である。

chmin(a,b,c)は[a,b]に対してseg_m=min(seg_m,c)を行う関数である。

頂点iを見るときのことを考える。

 左下からの移動は、dist_i=seg.RMQ(0,y_i)+cost_iである。

左下への移動は、これまで見た頂点のy座標の最小値をmin_yとするとseg.chmin(min_y,y_i,dist_i+cost_i)である。

 

 実際にACした提出

atcoder.jp