JOI2019/2020 本選 問題4 オリンピックバス (Olympic Bus) をΟ((M+N)logM+N^3)で解く
公式解説が出るまでの気休め
問題概要
頂点 辺からなる有向グラフが与えられる。頂点には から までの番号がついている。 番目の辺は頂点 から への辺で、長さは 、重みは である。次の操作を高々 回だけ行える。
- 整数 を つ選んで、 番目の辺の向きを逆にする。コストを 払う。
操作の後、次で定義されるコストを払う。
- 頂点 から頂点 までの最短距離と、頂点 から頂点 までの最短距離の和
- ただし、経路がない時のコストは である。
払うコストの最小値を求めよ。
制約
解法
どの辺も逆にしないときのコストは普通に求まる。
辺 を逆にしたときのコストを、すべての について求めればよい。
辺 を逆にしたときの → の距離と → の距離を、別々に求めることにする。 → が求まるなら、同じようなことを → に対して行えばよいから、前者の場合だけを考える。
どの辺も逆にしないときの → の距離 を求めておく。辺 を逆にしたときの → の距離 は、 より小さくなるか、 より大きくなるか、 と等しい、のいずれかを満たす。
距離 が より小さくなる場合の検出
辺 を逆にして新たに発生する経路の中で最短なものは、 → の最短経路、 → の辺 、 → の最短経路を順につなげたものである。この経路の距離 を求めたい。
辺 を消したときの → の距離 と → の距離 が求まっていれば、 と求めることができる。この が より小さいときのみ、最短距離が小さくなって、 となる。
辺 を消したときの と を求めなければならないが、これはもとのグラフで頂点 および頂点 からダイクストラをすることで、すべての について および をまとめて求めることができる。
こうやって求めた , , には、辺 (向きを逆にしていないもの)を経路に使ってしまっている場合があるが、この場合は となり、影響がない。なぜなら、例えば の計算で辺 を使ってしまった場合、であり、 であるから、
となる。したがって、 の計算には影響を与えないことがわかる。
辺 を使わない条件のもとでの → の距離を とすると、 であり、辺 (向きを逆にしていないもの)を使わない本来求めるべき について、 である。つまり、辺 (向きを逆にしていないもの)を使ってしまったときは、 が より小さくならないために、距離が小さくなる検出をする必要がなく、また異常な値によって を更新することもない。
距離 が より大きくなる場合の検出
どの辺も逆にしないときの → の最短経路 を1つ選ぶ。
に含まれていない辺の向きを逆にしても、距離 の経路 が残っているのだから、 → の距離は より大きくならない。したがって、 に含まれる辺についてのみ、新しい最短距離を計算すればよい。
に含まれる辺 を逆にした場合について考える。辺 を逆にすると、 は つに分断されて、パス とパス に分かれる。パス に含まれる頂点集合を 、パス に含まれる頂点集合を とおく。パス は → ( 内の頂点) の最短経路を含んでいて、パス は ( 内の頂点) → の最短経路を含んでいることがわかる。(そうでないと、 が最短経路でなくなる)
新しい → の最短経路は、 の一部を通った後、 に含まれない辺をいくつか経由して、 の一部を通るような経路であることを示せる。 → のどの経路 についても、最後に訪れた 内の頂点 と、 よりも後に訪れた頂点の中で、最初に訪れた 内の頂点 がある。パス には → の最短経路が、パス には → の最短経路が含まれているのだから、 の一部を通って頂点 に移動し、 に含まれない辺を の通りにいくつか経由して頂点 に移動し、 の一部を通って頂点 に移動するような経路を考えると、この経路の距離はパス の距離以下であるからだ。
の一部を通るときに最後に訪れる頂点 と、 の一部を通るときに最初に訪れる頂点 を固定する。 → の距離 と、 → の距離 は、事前に計算しておける。 に含まれない辺をいくつか経由して頂点 から頂点 に移動する部分の最短距離を とすると、新しい最短距離は、である。 は、以下のようにして求めることができる。
元のグラフから に含まれる辺をすべて取り除く。このグラフにおいて、ワーシャルフロイドを用いれば、すべての の組について、最短距離 をまとめて計算できる。また、この結果は 内のどの辺 についても使いまわすことができる。つまり、ワーシャルフロイドを行う回数は 回だけでよい。
内の各辺 について、ありうる の組をすべて試していっても しかかからない。
距離 が と等しい場合の検出
上の つを行って、距離が小さくも大きくもならない場合は である。
計算量
ダイクストラを 回、ワーシャルフロイドを 回行えば解けて、計算量は である。
考察めっちゃつらいし実装がしんどすぎる
提出
オープンのコードをそのまま提出した
リファクタリングはしたくない