黒ココアさんのメモ置き場

メモを置いたり置かなかったり

ABC338 D - Island Tour

atcoder.jp

問題

  • N個の島があるよ
  • i番目の島と、i+1番目の島が繋がってるよ
  • N番目の島は1番目の島と橋で繋がってるよ
  • 島M個を回るツアーがあるよ
  • 橋の内どれか1本を壊したいんだけど、ツアーの長さの最小値はいくつになる?

成約

  • $ 3 \leq N \leq 2 \times 10^5 $
  • $ 2 \leq M \leq 2 \times 10^5 $

思考

  • 橋ごとに、「この橋壊したらコストどうなる?」を見ていきたい…
  • 愚直にツアーを毎回確認してたら、$O(NM)$になって吹っ飛ぶ
  • じゃあどうしよう…(本番はここで止まった。無事死亡)
  • ツアーの移動毎に見ていく?
  • 島$X_i$から島$X_{i+1}$に行く時、橋が壊れていない時は2パターンの経路があるよね
    • 経路1(反時計回り):渡る橋の数は$X_{i+1} - X_i$個
    • 経路2(時計回り):渡る橋野数は$N - (X_{i+1} - X_i)$個
      • 経路1で使わなかった橋の数で、経路2ですね
      • アナログ時計の文字盤考えると良いかも
  • じゃあ、経路1の長さ、経路2の長さが出た所で…
    • 経路1の方が、経路2より短い時!
      • 経路1で使用した橋を壊してしまうと、経路2を通らなければいけない
      • 経路1で使用していない橋を壊しても、経路1で行けるので問題なし!
    • 経路2の方が、経路1より短い時!
      • 経路2で使用した橋を壊してしまうと、経路1を通らなければいけない
      • 経路2で使用していない橋を壊しても、経路2で行けるので問題なし!
    • 欲しい値は「この橋壊したら、ツアーの最小経路はいくつになる?」なので…橋ごとに纏めたいね
    • 経路1で使用した橋 / 経路2で使用した橋は、区間で表すことができるね!
      • じゃあ区間の変動する箇所だけメモって、imos法で集計取ってあげたら、$O(N)$で間に合うね!

コード

N,M = map(int,input().split())
X_L = list(map(int, input().split()))

cost_L = [0]*(N+10)

for i in range(M-1):
    a,b = X_L[i],X_L[i+1]
    if a > b:
        a,b = b,a
    min_dist = min(b-a,N-(b-a))
    max_dist = max(b-a,N-(b-a))
    dist_diff = max_dist - min_dist
    if b-a < N+a-b:
        cost_L[0] += min_dist
        cost_L[a] += dist_diff
        cost_L[b] -= dist_diff
        cost_L[N+1] -= min_dist
    else:
        cost_L[0] += max_dist
        cost_L[a] -= dist_diff
        cost_L[b] += dist_diff
        cost_L[N+1] -= max_dist

for i in range(1,N+1):
    cost_L[i] += cost_L[i-1]
print(min(cost_L[1:N+1]))