Official

D - 最安経路 / Cheapest Route Editorial by admin

GPT 5.2 High

Overview

This is a problem where we need to find the minimum toll (shortest distance) from city \(1\) to any airport city on a graph where the travel cost of each road is given by “the product of the populations of the two endpoint cities \(M_a \times M_b\)”.

Analysis

  • Treating cities as vertices and roads as edges, the cost of moving from city \(a\) to an adjacent city \(b\) is the edge weight \(w(a,b)=M_a \times M_b\). Since roads are bidirectional, this is an undirected graph, but the cost is the same in both directions (since multiplication is commutative).
  • What we want to find is “the minimum cost from city \(1\) to reach any city in the airport city set \(\{E_1,\dots,E_P\}\)” = “the shortest distance among multiple destinations from the source.”

Why a Naive Approach Fails

  • Exploring all paths (e.g., enumerating all paths with DFS) causes an explosion in the number of paths, since the same cities and roads can be visited repeatedly.
  • BFS only guarantees shortest distances when all edge weights are equal. In this problem, \(M_a \times M_b\) can differ for each edge, so BFS would give incorrect answers.

Solution Strategy

  • Since all edge weights are non-negative (\(M_i \ge 1\), so the product is also positive), we can find shortest distances using Dijkstra’s algorithm.
  • Even when there are multiple destinations, by leveraging Dijkstra’s property of confirming vertices in order of shortest distance, the answer is determined the moment we first confirm an airport city (no cheaper airport can exist).

Example: Even if there are multiple airports, since the distances extracted from the priority queue are monotonically non-decreasing, the “first airport extracted” has the minimum distance among all airports.

Algorithm

  1. Manage whether each city is an airport using an array is_airport.
  2. If city \(1\) is an airport, no travel is needed and the answer is \(0\).
  3. Prepare a distance array dist with dist[1]=0 and all others set to \(\infty\).
  4. Insert (0, 1) into a priority queue (min-heap) and start Dijkstra’s algorithm.
  5. Extract (d, u) from the heap.
    • If the extracted d differs from dist[u], it is outdated information, so skip it.
    • If u is an airport city, d is the minimum toll, so output it and terminate.
  6. For all adjacent vertices v of u, compute the transition cost by adding \(M_u \times M_v\): $\( nd = d + M_u \times M_v \)$ and relax: if nd < dist[v], update and insert into the heap.
  7. Since it is always possible to reach some airport, the algorithm will terminate at some point.

Complexity

  • Time complexity: \(O((N+K)\log N)\)
    (Dijkstra’s algorithm: each edge is relaxed at most \(2K\) times, with \(\log N\) per heap operation)
  • Space complexity: \(O(N+K)\)
    (adjacency list, distance array, airport flags, heap)

Implementation Notes

  • “Terminate immediately upon reaching an airport” is crucial. Since Dijkstra’s algorithm confirms vertices in order of minimum distance, the first confirmed airport gives the answer.

  • In Python, since the input can be large, we use sys.stdin.buffer.read() for fast input.

  • The adjacency list is also implemented in array format (head/to/nxt, C-style) to run efficiently even for \(N,K \le 2\times 10^5\).

  • Since the maximum distance can become very large (products are added many times along a path), we use a sufficiently large value like INF=10**30.

    Source Code

import sys
import heapq

def main():
    data = sys.stdin.buffer.read()
    ndata = len(data)
    ptr = 0

    def next_int():
        nonlocal ptr
        while ptr < ndata and data[ptr] <= 32:
            ptr += 1
        sign = 1
        if ptr < ndata and data[ptr] == 45:
            sign = -1
            ptr += 1
        num = 0
        while ptr < ndata:
            c = data[ptr]
            if c <= 32:
                break
            num = num * 10 + (c - 48)
            ptr += 1
        return num * sign

    N = next_int()
    K = next_int()
    P = next_int()

    M = [0] * (N + 1)
    for i in range(1, N + 1):
        M[i] = next_int()

    head = [-1] * (N + 1)
    to = [0] * (2 * K)
    nxt = [0] * (2 * K)
    ei = 0
    for _ in range(K):
        u = next_int()
        v = next_int()
        to[ei] = v
        nxt[ei] = head[u]
        head[u] = ei
        ei += 1
        to[ei] = u
        nxt[ei] = head[v]
        head[v] = ei
        ei += 1

    is_airport = [False] * (N + 1)
    for _ in range(P):
        e = next_int()
        is_airport[e] = True

    if is_airport[1]:
        print(0)
        return

    INF = 10**30
    dist = [INF] * (N + 1)
    dist[1] = 0
    hq = [(0, 1)]

    while hq:
        d, u = heapq.heappop(hq)
        if d != dist[u]:
            continue
        if is_airport[u]:
            print(d)
            return
        mu = M[u]
        e = head[u]
        while e != -1:
            v = to[e]
            nd = d + mu * M[v]
            if nd < dist[v]:
                dist[v] = nd
                heapq.heappush(hq, (nd, v))
            e = nxt[e]

if __name__ == "__main__":
    main()

This editorial was generated by gpt-5.2-high.