ABC 338 Dの解法
公式の解説や参加者の解説記事を読んでも、何でこれで上手くいくのか理解できなかったので、自分なりに理解できた解法を書いてみる。
解法のアプローチは以下をベースにして考えた。(もしかしたら同じなのかもしれない)
例として5つの島(橋)、5箇所を巡るツアーを考えてみる。
島と橋の番号
5 5
2 5 3 4 1
橋5が使えない場合の最短ルート
とりあえず、このツアーについて、橋を閉鎖する全パターンの移動距離を橋0から5まで順番に洗い出してみる。
(数が少ないので机上でできる)
移動順[1] | 移動ルート | 橋0(=橋5)を閉鎖する | 橋1を閉鎖する | 橋2... | 橋3... | 橋4... | 橋5... |
---|---|---|---|---|---|---|---|
0 | 2→5 | 3(2>3>4>5) | 3 | 2(2>1>5) | 2 | 2 | 3(2>3>4>5) |
1 | 5→3 | 2(5>4>3) | 2 | 2 | 3(5>1>2>3) | 3 | 2(5>4>3) |
2 | 3→4 | 1(3>4) | 1 | 1 | 4(3>2>1>5>4) | 1(3>4) | 1 |
3 | 4→1 | 3(4>3>2>1) | 2(4>5>1) | 2 | 2 | 3(4>3>2>1) | 3 |
合計 | - | 9 | 8 | 7 | 11 | 9 | 9 |
橋2を閉鎖したときが最短距離で7となる。
ここからわかることは次の通り。
- 移動距離は必ず2パターン
- 橋は島をリング状に繋いでおり、その移動ルートは左回りか右回りの2パターンしかないため。
- 移動距離の2パターンは足すと
になるN - 橋は島をリング状に繋いでいるため、1周するには橋をすべて1度ずつ使う必要がある。島
から 島i を経由して1周することを考えると、 島j から島i まで右回りで進んだときの距離がj なら、また右回りで島l まで戻ってくる距離はi である。そして、これは島N - l から島i まで左回りで進んだときの区間と一致する。j
- 橋は島をリング状に繋いでいるため、1周するには橋をすべて1度ずつ使う必要がある。島
- 橋
を除けば、右回りか左回りかの変化は各移動順ごとで必ず2回だけ起こる0 - 島
を右回りに移動するとき、必ずi→j (i<j) と移動することになり、橋の閉鎖の仕方もi, i+1, i+2, ... , j-1, j と順番にしているため、橋の閉鎖が0, 1, ... , N に差し掛かると右回りが使えなくなり、橋の閉鎖がi まで到達するとまた右回りができるようになる。j+1
- 島
- 回り方の変化が起こる場所は移動ルートの島番号と一致する
- 変化する場所が島番号と一致するのは、島番号
を橋番号i→i+1 としたため。i を橋番号i→i+1 とする考え方でも解けるが、前者のほうがより直感的だと思ったのでこうした。i+1
- 変化する場所が島番号と一致するのは、島番号
-
今後のコードの都合で0始まりにしている ↩︎
わかったことからアプローチを考えてみる。
-
の各移動順について、橋が閉鎖されたことで回り方の向きが変わる場所を洗い出すX_1→X_2, X_2→X_3, ... , X_{M-1}→X_M - 移動順の数は
なのでM-1 O(M)
- 移動順の数は
- 橋
(=橋0 )を閉鎖した場合の各移動順の移動距離を計算するN - これも
なのでM-1 O(M)
- これも
- 橋
を閉鎖した場合、橋i(i=1,2,...,N-1) から回り方の向きが変わることでの距離変化を計算するi-1 - 以下の処理を
回行う必要があるのでN-1 (O(Nn) は各橋での計算量)n - 変換点の取得は各橋で必要。手順1で効率の良いデータ構造を使えば
O(1) - 変換点での距離変化の計算をして、橋
で計算した距離から差分更新をする。この計算は各橋で必ずしも必要になるわけではなく、全体としてi-1 [1] なので、各橋で見た場合は2M O(\frac{M}{N}) - よって
n = 1+\frac{M}{N}
- 変換点の取得は各橋で必要。手順1で効率の良いデータ構造を使えば
- よって
O(N(1+\frac{M}{N})) = O(N+M)
- 以下の処理を
- 2と3で計算した各移動距離のうち最短のものを出力する
O(N)
ということで、
-
ここについては例として100の島と、10万の島に対して訪問する島が3箇所だけという状況で考えてみるとわかりやすい。回り方が変わることによる再計算は6回であるため、橋の閉鎖によって回り方が変わる橋が完全に相違だったとしても、前者では94、後者では99,994の橋では閉鎖による回り方の変化は起きないことになる。つまり、この計算が必要な頻度は、
が同じであれば、M が大きいほど少なくなるはずである。 ↩︎N
0 (=橋N )を閉鎖した場合の各移動順の移動距離を計算する
ステップ2: 橋これ単に下記の繋がり方をした島の移動距離を考えればいいだけである。
島1 ー 島2 ー 島3 ー 島4 ー 島5
単純な等差数列になっているので、例えば移動順0(島2→島5)の移動であれば
一般化すれば
である。
i(i=1,2,...,N-1) を閉鎖した場合、橋i-1 から回り方の向きが変わることでの距離変化を計算する
ステップ3: 橋前述した通り、橋
橋3を例に考えてみる。橋2での閉鎖では
移動順 | 移動ルート | 橋2... |
---|---|---|
0 | 2→5 | 2(2>1>5) |
1 | 5→3 | 2 |
2 | 3→4 | 1 |
3 | 4→1 | 2 |
(一部抜粋して再掲)
となっており、その移動距離は
橋3の閉鎖では、移動順1と移動順2で回り方が変わることになるため、それぞれ計算してみる。
- 移動順1: 橋2の閉鎖では2だったので
、つまり橋2の距離から見ると5-2=3 になった+1 - 移動順2: 橋2の閉鎖では1だったので
、つまり橋2の距離から見ると5-1=4 になった+3
この2つの変化はトータルで考えると
同じことを橋
コードはこちら。