Number of Restricted Paths From First to Last Node

medium graph shortest path dynamic programming

Problem

An undirected weighted connected graph has n nodes labeled 1..n. Let distanceToLastNode(x) be the shortest-path distance from node n to node x. A path [z0, …, zk] is restricted when every step strictly decreases that distance: distanceToLastNode(zi) > distanceToLastNode(zi+1). Return the number of restricted paths from node 1 to node n, modulo 109 + 7.

Inputn = 5, edges = [[1,2,3],[1,3,3],[2,3,1],[1,4,2],[5,2,2],[3,5,1],[5,4,10]]
Output3
The restricted paths are 1→2→5, 1→2→3→5, and 1→3→5.

import heapq

def countRestrictedPaths(n, edges):
    MOD = 10**9 + 7
    g = [[] for _ in range(n + 1)]            # adjacency list
    for u, v, w in edges:
        g[u].append((v, w)); g[v].append((u, w))
    # Dijkstra from node n: dist[x] = shortest distance to n
    dist = [float('inf')] * (n + 1)
    dist[n] = 0
    pq = [(0, n)]
    while pq:
        d, u = heapq.heappop(pq)
        if d > dist[u]:
            continue
        for v, w in g[u]:
            if d + w < dist[v]:
                dist[v] = d + w
                heapq.heappush(pq, (dist[v], v))
    # Count restricted paths with DP, nodes in increasing dist order
    ways = [0] * (n + 1)
    ways[n] = 1
    order = sorted(range(1, n + 1), key=lambda x: dist[x])
    for u in order:
        for v, w in g[u]:
            if dist[u] > dist[v]:               # step decreases distance
                ways[u] = (ways[u] + ways[v]) % MOD
    return ways[1] % MOD
function countRestrictedPaths(n, edges) {
  const MOD = 1000000007n;
  const g = Array.from({ length: n + 1 }, () => []);   // adjacency list
  for (const [u, v, w] of edges) { g[u].push([v, w]); g[v].push([u, w]); }
  // Dijkstra from node n: dist[x] = shortest distance to n
  const dist = new Array(n + 1).fill(Infinity);
  dist[n] = 0;
  const pq = [[0, n]];                                 // simple binary-search heap
  while (pq.length) {
    const [d, u] = pq.shift();
    if (d > dist[u]) continue;
    for (const [v, w] of g[u]) {
      if (d + w < dist[v]) {
        dist[v] = d + w;
        let lo = 0, hi = pq.length;
        while (lo < hi) { const m = (lo + hi) >> 1; pq[m][0] < dist[v] ? lo = m + 1 : hi = m; }
        pq.splice(lo, 0, [dist[v], v]);
      }
    }
  }
  // Count restricted paths with DP, nodes in increasing dist order
  const ways = new Array(n + 1).fill(0n);
  ways[n] = 1n;
  const order = [...Array(n).keys()].map(i => i + 1).sort((a, b) => dist[a] - dist[b]);
  for (const u of order)
    for (const [v, w] of g[u])
      if (dist[u] > dist[v]) ways[u] = (ways[u] + ways[v]) % MOD;
  return Number(ways[1] % MOD);
}
int countRestrictedPaths(int n, int[][] edges) {
    final int MOD = 1_000_000_007;
    List<long[]>[] g = new List[n + 1];                 // adjacency list
    for (int i = 0; i <= n; i++) g[i] = new ArrayList<>();
    for (int[] e : edges) {
        g[e[0]].add(new long[]{e[1], e[2]});
        g[e[1]].add(new long[]{e[0], e[2]});
    }
    // Dijkstra from node n: dist[x] = shortest distance to n
    long[] dist = new long[n + 1];
    Arrays.fill(dist, Long.MAX_VALUE);
    dist[n] = 0;
    PriorityQueue<long[]> pq = new PriorityQueue<>((a, b) -> Long.compare(a[0], b[0]));
    pq.add(new long[]{0, n});
    while (!pq.isEmpty()) {
        long[] top = pq.poll();
        long d = top[0]; int u = (int) top[1];
        if (d > dist[u]) continue;
        for (long[] e : g[u]) {
            int v = (int) e[0]; long w = e[1];
            if (d + w < dist[v]) { dist[v] = d + w; pq.add(new long[]{dist[v], v}); }
        }
    }
    // Count restricted paths with DP, nodes in increasing dist order
    long[] ways = new long[n + 1];
    ways[n] = 1;
    Integer[] order = new Integer[n];
    for (int i = 0; i < n; i++) order[i] = i + 1;
    Arrays.sort(order, (a, b) -> Long.compare(dist[a], dist[b]));
    for (int u : order)
        for (long[] e : g[u]) {
            int v = (int) e[0];
            if (dist[u] > dist[v]) ways[u] = (ways[u] + ways[v]) % MOD;
        }
    return (int) (ways[1] % MOD);
}
int countRestrictedPaths(int n, vector<vector<int>>& edges) {
    const long long MOD = 1e9 + 7;
    vector<vector<pair<int,int>>> g(n + 1);              // adjacency list
    for (auto& e : edges) {
        g[e[0]].push_back({e[1], e[2]});
        g[e[1]].push_back({e[0], e[2]});
    }
    // Dijkstra from node n: dist[x] = shortest distance to n
    vector<long long> dist(n + 1, LLONG_MAX);
    dist[n] = 0;
    priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<>> pq;
    pq.push({0, n});
    while (!pq.empty()) {
        auto [d, u] = pq.top(); pq.pop();
        if (d > dist[u]) continue;
        for (auto& [v, w] : g[u])
            if (d + w < dist[v]) { dist[v] = d + w; pq.push({dist[v], v}); }
    }
    // Count restricted paths with DP, nodes in increasing dist order
    vector<long long> ways(n + 1, 0);
    ways[n] = 1;
    vector<int> order(n);
    for (int i = 0; i < n; i++) order[i] = i + 1;
    sort(order.begin(), order.end(), [&](int a, int b){ return dist[a] < dist[b]; });
    for (int u : order)
        for (auto& [v, w] : g[u])
            if (dist[u] > dist[v]) ways[u] = (ways[u] + ways[v]) % MOD;
    return (int)(ways[1] % MOD);
}
Time: O((V + E) log V) Space: O(V + E)