Official
D - 荷物の配送 / Package Delivery Editorial by admin
GPT 5.2 High概要
道路で結ばれた拠点間について、\(N\) 個の配送依頼 \((S_i, T_i)\) それぞれの最短距離を求め、その合計を出力する問題です。
考察
- 求めたいのは「同じグラフ上で、たくさんの始点・終点ペアの最短距離」です。
つまり クエリ(配送依頼)が \(N=10^5\) と非常に多い のがポイントです。 - 素朴に「依頼ごとにダイクストラ法」をすると、1回あたり \(O(K\log M)\) 程度かかります。これを \(10^5\) 回繰り返すと到底間に合いません。
- ここで拠点数 \(M \le 200\) が小さいことに注目します。
全点対間最短距離(すべての \((u,v)\) の最短距離)を前計算してしまえば、各依頼は表から \(O(1)\) で取り出して足すだけになります。 - 全点対間最短距離の典型手法は ワーシャル–フロイド法 で、計算量は \(O(M^3)\)。
\(M=200\) なら \(200^3=8,000,000\) 程度なので十分高速です。
(例)
拠点が少ないなら、先に「拠点1→拠点2の最短距離」「拠点1→拠点3の最短距離」…を全部埋めておき、配送依頼ごとに該当セルを見るだけ、というイメージです。
アルゴリズム
- 距離行列
distを用意する(サイズ \(M \times M\))。- 初期値は \(\infty\)(十分大きい値)
dist[i][i] = 0
- 各道路 \((U_j, V_j, W_j)\) について、
dist[U_j][V_j] = W_j,dist[V_j][U_j] = W_j(無向)
- ワーシャル–フロイド法で全点対最短距離を計算する:
- 中継点 \(k\) を 1 つずつ増やしながら
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])を全 \((i,j)\) に適用する。
- 中継点 \(k\) を 1 つずつ増やしながら
- 各配送依頼 \((S_i, T_i)\) について
dist[S_i][T_i]を加算し、合計を出力する。
計算量
- 時間計算量: \(O(M^3 + N)\)
(前計算が \(O(M^3)\)、各クエリの加算が \(O(1)\) なので合計 \(O(N)\)) - 空間計算量: \(O(M^2)\)
(距離行列dist)
実装のポイント
インデックスを 0-based に変換して扱う(入力は 1-based)。
距離の最大値は大きくなりうるため、\(\infty\) には
10**18など十分大きい値を使う。ワーシャル–フロイド法では
dist[i][k]が \(\infty\) のときに更新をスキップすると少し高速化できる(コード中のif aik == INF: continue)。入力が大きいので
sys.stdin.buffer.read()でまとめて読み、速度を確保する。ソースコード
import sys
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
N = next(it)
M = next(it)
K = next(it)
INF = 10**18
dist = [[INF] * M for _ in range(M)]
for i in range(M):
dist[i][i] = 0
for _ in range(K):
u = next(it) - 1
v = next(it) - 1
w = next(it)
if w < dist[u][v]:
dist[u][v] = w
dist[v][u] = w
for k in range(M):
dk = dist[k]
for i in range(M):
di = dist[i]
aik = di[k]
if aik == INF:
continue
for j in range(M):
nd = aik + dk[j]
if nd < di[j]:
di[j] = nd
ans = 0
for _ in range(N):
s = next(it) - 1
t = next(it) - 1
ans += dist[s][t]
sys.stdout.write(str(ans))
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
posted:
last update: