Number of Ways to Arrive at Destination
WHAT IT SAYS
Count the distinct shortest-time routes from intersection 0 to intersection n−1 in a weighted road network, modulo 1e9+7.
WHAT IT'S REALLY ASKING
"Shortest routes compose: every shortest route into v arrives through some neighbour u where dist[u] + edge = dist[v], and it extends one of u's shortest routes. So a counter can ride shotgun on Dijkstra — strictly better arrival: inherit the count; exactly equal arrival: ADD the counts. Enumerate nothing; the ways aggregate as the distances settle."
Find the shortest time, then enumerate matching routes
Exponential — route enumeration ignores everything the distances provedRun Dijkstra for the optimal time, then DFS all routes from source to destination, counting those whose total equals the optimum.
WHERE THE WORK IS WASTED — Counting by listing is the cardinal sin when counts compose: the number of shortest routes to v is fully determined by the numbers at its shortest-path predecessors — a sum, computable in O(degree) — yet enumeration materialises every route individually, re-walking shared prefixes once per route that uses them. A graph can pack exponentially many shortest routes into linearly many edges (diamond chains double the count per stage); enumeration pays per route, arithmetic pays per edge. Word Ladder II had to enumerate because it wanted the paths themselves; this problem asks only HOW MANY, and the downgrade from 'list' to 'count' is precisely what makes the exponential collapse available.
Ways compose over the shortest-path DAG — sum at equality.
The structural fact underneath: keep exactly the edges (u, v) satisfying dist[u] + w(u,v) = dist[v] — the 'tight' edges — and they form a DAG (dist strictly increases along each, so no cycles), whose source-to-v paths are IN BIJECTION with the shortest routes to v. Every shortest route uses only tight edges (a slack edge anywhere would overshoot the total), and every tight-edge path from the source totals exactly dist[v]. So ways[v] obeys clean DAG arithmetic: ways[source] = 1 (the empty route), and ways[v] = sum of ways[u] over tight in-edges — counts flowing forward through the DAG like Pascal's rule, because route sets through distinct predecessors are disjoint (different last edge) and exhaustive. Now the fusion that removes even the second pass: Dijkstra settles nodes in non-decreasing dist order, which is a valid topological order of the tight DAG — every tight predecessor of v settles before v (strictly smaller dist... with the equal-dist edge-weight-zero caveat absent here since weights are positive). So the sum can be accumulated DURING relaxation, and the two relaxation outcomes map exactly onto counting rules. Strict improvement (dist[u] + w < dist[v]): all previously counted routes to v just became non-shortest — obsolete — so OVERWRITE: ways[v] = ways[u]. Exact tie (dist[u] + w == dist[v]): a new family of shortest routes arriving via u, disjoint from those already counted (different final edge) — ADD: ways[v] += ways[u]. The overwrite-versus-add pair is the entire algorithm; everything else is Network Delay Time's engine unchanged. Two disciplines keep it honest. Modulo 1e9+7 on every addition — the counts are the exponential objects here even though the computation is polynomial, and they overflow fast. And only trust accumulations at SETTLED sources: with lazy-deletion Dijkstra, process a node's outgoing relaxations when it is popped fresh (first pop = settled = ways[u] final); adding from a node whose own count later grows would undercount downstream. The invariant to keep in your head: ways[v] is provisional until v settles, final after — mirroring dist[] exactly, which is why they can share one pass.
Dijkstra with a parallel ways[] and the overwrite/add rule
O(E log V) time — Dijkstra's own bill — O(V) extra for the counter arraydist[] = infinity, ways[] = 0, dist[0] = 0, ways[0] = 1; heap (0, 0). Pop fresh (skip stale); for each edge (u, v, w): nd = dist[u] + w; if nd < dist[v]: dist[v] = nd, ways[v] = ways[u], push; else if nd == dist[v]: ways[v] = (ways[v] + ways[u]) mod M. Use 64-bit times (weights up to 1e9 across 200 nodes). Return ways[n−1].
Overwrite on Improve, Add on Tie
YOU'LL SEE IT AGAIN WHEN
- COUNT of optimal solutions is asked — not the solutions themselves — so per-node counters can replace enumeration entirely.
- Tight edges (where the value recurrence holds with equality) form a DAG, and counts compose disjointly across a node's tight predecessors.
- The optimiser's settle order is a topological order of that DAG — the counter can ride the same pass if sources are only trusted once settled.