Closed7

ABC 338 Dの解法

tkzwhrtkzwhr

公式の解説や参加者の解説記事を読んでも、何でこれで上手くいくのか理解できなかったので、自分なりに理解できた解法を書いてみる。

解法のアプローチは以下をベースにして考えた。(もしかしたら同じなのかもしれない)

https://atcoder.jp/contests/abc338/editorial/9186

tkzwhrtkzwhr

例として5つの島(橋)、5箇所を巡るツアーを考えてみる。

島と橋の番号
島と橋の番号

5 5
2 5 3 4 1

橋5が使えない場合の最短ルート
橋5が使えない場合の最短ルート

tkzwhrtkzwhr

とりあえず、このツアーについて、橋を閉鎖する全パターンの移動距離を橋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から 島jを経由して1周することを考えると、 島iから島jまで右回りで進んだときの距離がlなら、また右回りで島iまで戻ってくる距離はN - lである。そして、これは島iから島jまで左回りで進んだときの区間と一致する。
  • 0を除けば、右回りか左回りかの変化は各移動順ごとで必ず2回だけ起こる
    • 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とする考え方でも解けるが、前者のほうがより直感的だと思ったのでこうした。
脚注
  1. 今後のコードの都合で0始まりにしている ↩︎

tkzwhrtkzwhr

わかったことからアプローチを考えてみる。

  1. X_1→X_2, X_2→X_3, ... , X_{M-1}→X_M の各移動順について、橋が閉鎖されたことで回り方の向きが変わる場所を洗い出す
    • 移動順の数はM-1なのでO(M)
  2. 0(=橋N)を閉鎖した場合の各移動順の移動距離を計算する
    • これもM-1なのでO(M)
  3. i(i=1,2,...,N-1)を閉鎖した場合、橋i-1から回り方の向きが変わることでの距離変化を計算する
    • 以下の処理をN-1回行う必要があるので O(Nn)nは各橋での計算量)
      • 変換点の取得は各橋で必要。手順1で効率の良いデータ構造を使えばO(1)
      • 変換点での距離変化の計算をして、橋i-1で計算した距離から差分更新をする。この計算は各橋で必ずしも必要になるわけではなく、全体として2M[1] なので、各橋で見た場合はO(\frac{M}{N})
      • よってn = 1+\frac{M}{N}
    • よってO(N(1+\frac{M}{N})) = O(N+M)
  4. 2と3で計算した各移動距離のうち最短のものを出力する
    • O(N)

ということで、 O(N+M)で解ける。

脚注
  1. ここについては例として100の島と、10万の島に対して訪問する島が3箇所だけという状況で考えてみるとわかりやすい。回り方が変わることによる再計算は6回であるため、橋の閉鎖によって回り方が変わる橋が完全に相違だったとしても、前者では94、後者では99,994の橋では閉鎖による回り方の変化は起きないことになる。つまり、この計算が必要な頻度は、Mが同じであれば、Nが大きいほど少なくなるはずである。 ↩︎

tkzwhrtkzwhr

ステップ1: X_1→X_2, X_2→X_3, ... , X_{M-1}→X_M の各移動順について、橋が閉鎖されたことで回り方の向きが変わる場所を洗い出す

移動順 移動ルート
0 2→5
1 5→3
2 3→4
3 4→1

移動順になっているデータを、各橋ごとに変換点となっている移動順の対応として持てばよい。これは連想配列を使えばできる。

橋番号 この橋が変換点となっている移動順
1 3
2 0
3 1, 2
4 2, 3
5[1] 0, 1
脚注
  1. 橋Nは橋0として最初に計算してしまう関係で、実際にはこのデータは使わない ↩︎

tkzwhrtkzwhr

ステップ2: 橋0(=橋N)を閉鎖した場合の各移動順の移動距離を計算する

これ単に下記の繋がり方をした島の移動距離を考えればいいだけである。

島1 ー 島2 ー 島3 ー 島4 ー 島5

単純な等差数列になっているので、例えば移動順0(島2→島5)の移動であれば|2-5|=3で、移動順1(島5→島3)の移動であれば|5-3|=2である。

一般化すれば

distance(移動順i) = |X_i - X_{i+1}|

である。

tkzwhrtkzwhr

ステップ3: 橋i(i=1,2,...,N-1)を閉鎖した場合、橋i-1から回り方の向きが変わることでの距離変化を計算する

前述した通り、橋iが回り方の変換点であるならば、その距離は橋i-1の距離をlとするとN-lに変化する。

橋3を例に考えてみる。橋2での閉鎖では

移動順 移動ルート 橋2...
0 2→5 2(2>1>5)
1 5→3 2
2 3→4 1
3 4→1 2

(一部抜粋して再掲)

となっており、その移動距離は2+2+1+2=7であった。

橋3の閉鎖では、移動順1と移動順2で回り方が変わることになるため、それぞれ計算してみる。

  • 移動順1: 橋2の閉鎖では2だったので5-2=3、つまり橋2の距離から見ると+1になった
  • 移動順2: 橋2の閉鎖では1だったので5-1=4、つまり橋2の距離から見ると+3になった

この2つの変化はトータルで考えると+4の距離変化であると分かり、その総距離は7→11と変化したことがわかる。

同じことを橋1から橋N-1まで順次調べていけば、すべての橋を閉鎖したパターンについて総距離を求めることができ、最初に行っていた机上計算と同じ表を作れたことになる🎉

コードはこちら。

https://atcoder.jp/contests/abc338/submissions/49753140

このスクラップは3ヶ月前にクローズされました