公式

D - 混雑する交差点 / Congested Intersection 解説 by admin

Gemini 3.0 Flash (Thinking)

Overview

This problem asks us to find the shortest travel time from intersection \(1\) to intersection \(N\) in a graph consisting of \(N\) intersections and \(M\) roads. The difference from a standard shortest path problem is the special cost rule: “passing through an intersection whose degree (number of connected roads) is \(K\) or more costs an additional 1 minute.”

Analysis

1. Organizing the Costs

The time required for travel can be organized as follows: - Traversing one road: always costs 1 minute. - Arriving at intersection \(i\) and then heading to the next intersection (passing through): - If the degree of intersection \(i\) is \(K\) or more, an additional 1 minute is incurred. - However, this additional time does not apply at the starting point (intersection \(1\)) or the destination (intersection \(N\)).

This “additional time when passing through” becomes easier to handle if we interpret it as “a cost incurred when entering that intersection.” Specifically, when moving from intersection \(u\) to intersection \(v\), we define the cost as follows: - When \(v = N\): cost = \(1\) (road only) - When \(v \neq N\) and degree of \(v \ge K\): cost = \(1 + 1 = 2\) (road + additional time) - Otherwise: cost = \(1\) (road only) ※ Even if the degree of the starting intersection 1 is \(K\) or more, no additional time is incurred when “departing” from it, so it does not need to be considered.

2. Why Simple BFS Cannot Solve This

Standard breadth-first search (BFS) can only correctly find shortest paths when all edge costs are equal. In this problem, movement costs are a mix of \(1\) and \(2\), so instead of simple BFS, we need to use Dijkstra’s algorithm.

Algorithm

  1. Graph Construction and Degree Calculation: Store the graph in adjacency list format, and simultaneously count the degree (number of connected edges) of each vertex.
  2. Determining Additional Costs: For each intersection \(i \in \{2, \dots, N-1\}\), check whether its degree is \(K\) or more, and store whether additional cost is incurred using a flag or array.
  3. Shortest Path Search Using Dijkstra’s Algorithm:
    • Prepare a priority queue (heap) that manages (elapsed time, current intersection).
    • Set the time at the starting point (intersection 1) to 0 and add it to the queue.
    • Extract the vertex \(u\) with minimum time \(d\) from the queue, and update the time for each adjacent vertex \(v\):
      • new_time = d + 1 + (1 if v is an intermediate point and congested, else 0)
      • If new_time is less than the current dist[v], update it and add to the queue.
  4. Output the Result: Output the minimum time to reach intersection \(N\). If it is never reached, output -1.

Complexity

  • Time Complexity: \(O((N + M) \log N)\)
    • Degree calculation takes \(O(M)\), and Dijkstra’s algorithm takes \(O((N + M) \log N)\). This runs sufficiently fast for the constraints \(N, M \leq 2 \times 10^5\).
  • Space Complexity: \(O(N + M)\)
    • \(O(N + M)\) memory is used to store the adjacency list, distance array, and degree array.

Implementation Notes

  • Fast Input: Since \(N, M\) can be large, in Python using sys.stdin.read().split() to read all input at once can reduce execution time.

  • Exclusion Conditions for Additional Cost: Do not forget the condition stated in the problem: “no additional time is incurred at the starting point 1 and the destination \(N\).” In code, this can be handled by excluding intersection \(N\) from the check, or by treating \(v=N\) as a special case during cost calculation.

  • Dijkstra’s Algorithm Basics: Elements extracted from the queue with a cost greater than the already finalized shortest distance should be ignored (if d > dist[u]: continue) to avoid unnecessary computation.

    Source Code

import sys
import heapq

def solve():
    # Fast input reading using sys.stdin.read().split()
    input_data = sys.stdin.read().split()
    if not input_data:
        return
    
    it = iter(input_data)
    try:
        N = int(next(it))
        M = int(next(it))
        K = int(next(it))
    except StopIteration:
        return
    
    # Building the adjacency list and calculating the degree of each intersection
    adj = [[] for _ in range(N + 1)]
    degree = [0] * (N + 1)
    for _ in range(M):
        try:
            u = int(next(it))
            v = int(next(it))
            adj[u].append(v)
            adj[v].append(u)
            degree[u] += 1
            degree[v] += 1
        except StopIteration:
            break
            
    # extra_cost[v] is 1 if intersection v incurs an additional minute.
    # This applies only when "passing through" an intersection, meaning it
    # must be an intermediate node in the path. The problem states that 
    # additional time is not required at the starting intersection (1) 
    # or the arrival intersection (N).
    extra_cost = [0] * (N + 1)
    for i in range(2, N):
        if degree[i] >= K:
            extra_cost[i] = 1
            
    # Dijkstra's algorithm to find the minimum time from intersection 1 to N
    # dist[i] stores the minimum time taken to reach intersection i
    INF = 10**18
    dist = [INF] * (N + 1)
    dist[1] = 0
    pq = [(0, 1)] # (current_time, current_intersection)
    
    while pq:
        d, u = heapq.heappop(pq)
        
        # If a shorter path to u has already been processed, skip this one
        if d > dist[u]:
            continue
        
        # If we have reached the destination intersection N, output the time
        if u == N:
            print(d)
            return
        
        for v in adj[u]:
            # The time to travel along any road is 1 minute.
            # If the destination intersection v is an intermediate intersection
            # (i.e., v is not N), we add the extra_cost associated with v.
            # If v is N, extra_cost[v] is already 0.
            new_time = d + 1 + extra_cost[v]
            if dist[v] > new_time:
                dist[v] = new_time
                heapq.heappush(pq, (new_time, v))
                
    # If intersection N is unreachable from intersection 1
    print("-1")

if __name__ == '__main__':
    solve()

This editorial was generated by gemini-3-flash-thinking.

投稿日時:
最終更新: