143AC - SoundHound (D): Saving Snuuk

SoundHound-D (400 points)
問題
  • すぬけ国には  n 個の都市  (1, 2, \ldots, n) と, 都市間をつなぐ  m 本の電車がある.
  •  i 番目の電車は都市  u_i v_i を両方向に走っている.
  •  i 番目の電車の運賃は  a_i 円 /  b_i スヌークである(円とスヌークはすぬけ国の通貨).
  • 両替所では  1 円を  1 スヌークに両替可能だが, 持っている円を全てスヌークに両替しなければならない. スヌーク→円の両替は不可.
  • 現在は全ての都市に両替所が存在するが, 都市  i の両替所は  i 年後に閉鎖.
  •  i 年後に 10^{15} 円を持って都市  s から  t に, どこかで円をスヌークに両替しながら旅行するとき,  i = 0, 1, \ldots, n - 1 のそれぞれについて, 都市  t 到着時点の手持ちスヌークの最大値を求める.
  •  2 \leq  n \leq 10^5
  •  1 \leq m \leq 10^5
  •  1 \leq s, t \leq n
  •  s \neq t
  •  1 \leq u_i < v_i \leq n
  •  1 \leq a_i, b_i \leq 10^9
  •  i \neq j のとき  u_i \neq u_j または  v_i \neq v_j
  • どの都市からどの都市へも電車を乗り継ぐことで到達可能
  • 入力は全て整数
方針
  •  s \to 両替  \to t
  • 両替までは円, 両替後はスヌークなので, 辺のコストを  a_i, b_i としたグラフをそれぞれ構築
  • それぞれのグラフ上で s, t からの最短路をダイクストラ法で求める.
  • どこの両替所を使うのがベストか決める.
  • 両替所の数が多いと面倒なので, 都市  n しか使えない状態から考えて, 帰納法っぽく都市を増やしていく.
  • 都市  n, n - 1, n - i が使える場合に使うべき両替所 = 都市  n - i or 都市  n, n - 1, n - i + 1 が使える場合に使うべき両替所
import heapq

# dijkstra
# edges = |V|のリスト
# 頂点iから頂点jにコストwで移動可能な場合:edges[i] = [(j, w)]

def dijkstra(n, edges, start):
    INF = float('inf')
    vertices = [INF] * n # 初期化
    vertices[start] = 0 # スタートの距離はゼロ
    hq = [(0, start)]
    heapq.heapify(hq)

    while hq:
        cost, v = heapq.heappop(hq)

        for to, w in edges[v]: # vとの間にedgeが存在する頂点のコストを更新
            if cost + w >= vertices[to]: # 最短距離が更新されない場合
                continue
            vertices[to] = cost + w # 最短距離が更新される場合
            heapq.heappush(hq, (vertices[to], to))

    return vertices

# input
n, m, s, t = map(int, input().split())
G = [list(map(int, input().split())) for _ in range(m)]
INF = float('inf')

# 円とスヌークそれぞれについてグラフを構築
G_yen = [[] for _ in range(n)]
G_snu = [[] for _ in range(n)]

s, t = s - 1, t - 1

for u, v, a, b in G:
    G_yen[u - 1].append((v - 1, a))
    G_yen[v - 1].append((u - 1, a))
    G_snu[u - 1].append((v - 1, b))
    G_snu[v - 1].append((u - 1, b))

# 円/スヌークの場合のs/tからの最短距離
yen = dijkstra(n, G_yen, s)
snu = dijkstra(n, G_snu, t)

ans = [INF]

# 時間を逆から(経由地=両替所の選択肢が少ない順)
for i in reversed(range(n)):
    tmp = snu[i] + yen[i]
    ans.append(min(ans[-1], tmp))

for i in reversed(ans[1:]):
    print(10 ** 15 - i)