Dijkstra + DFS + DP
javascript []\nclass MinMaxPriorityQueue {\n\n  #heap = []\n  #compare;\n\n  constructor(compare) {\n    this.#heap = [];\n    this.#compare = compare || ((a, b
```javascript []\nclass MinMaxPriorityQueue {\n\n  #heap = []\n  #compare;\n\n  constructor(compare) {\n    this.#heap = [];\n    this.#compare = compare || ((a, b) => a - b) //By default, keep it MinHeap\n  }\n\n  // Add a new element to the priority queue\n  enqueue = (value) => {\n    this.#heap.push(value);\n    this.#heapifyUp(this.#heap.length - 1);\n  }\n\n  // Remove and return the element with the highest priority (smallest for min-heap, largest for max-heap)\n  dequeue = () => {\n    if (this.#heap.length === 0) return null;\n    const root = this.#heap[0];\n    const end = this.#heap.pop();\n    if (this.#heap.length > 0) {\n      this.#heap[0] = end;\n      this.#heapifyDown(0);\n    }\n    return root;\n  }\n\n  // Peek at the element with the highest priority without removing it\n  peek = () => {\n    return this.#heap.length > 0 ? this.#heap[0] : null;\n  }\n\n  // Check if the priority queue is empty\n  isEmpty = () => {\n    return this.#heap.length === 0;\n  }\n\n  // Get the size of the priority queue\n  size = () => {\n    return this.#heap.length;\n  }\n\n  // Maintain heap property by moving element up\n  #heapifyUp = (index) => {\n    let parent = Math.floor((index - 1) / 2);\n    while (index > 0 && this.#compare(this.#heap[index], this.#heap[parent]) < 0) {\n      this.#swap(index, parent)\n      index = parent;\n      parent = Math.floor((index - 1) / 2);\n    }\n  }\n\n  // Maintain heap property by moving element down\n  #heapifyDown = (index) => {\n    const length = this.#heap.length;\n    let left = 2 * index + 1;\n    let right = 2 * index + 2;\n    let extreme = index;\n\n    if (left < length && this.#compare(this.#heap[left], this.#heap[extreme]) < 0) {\n      extreme = left;\n    }\n\n    if (right < length && this.#compare(this.#heap[right], this.#heap[extreme]) < 0) {\n      extreme = right;\n    }\n\n    if (extreme !== index) {\n      this.#swap(index, extreme)\n      this.#heapifyDown(extreme);\n    }\n  }\n\n  #swap = (i, j) => {\n    [this.#heap[i], this.#heap[j]] = [this.#heap[j], this.#heap[i]]\n  }\n}\n\n/**\n * @param {number} n\n * @param {number[][]} edges\n * @return {number}\n */\nconst countRestrictedPaths = function (n, edges) {\n  const MOD = 1e9 + 7;\n\n  // Build adjacency list\n  const graph = Array.from({ length: n + 1 }, () => []);\n  for (const [u, v, weight] of edges) {\n    graph[u].push([v, weight]);\n    graph[v].push([u, weight]);\n  }\n\n  // Step 1: Dijkstra\'s Algorithm to compute shortest distances from node `n`\n  const dist = Array(n + 1).fill(Infinity);\n  dist[n] = 0;\n\n  // Using MinMaxPriorityQueue for Dijkstra\n  const pq = new MinMaxPriorityQueue((a, b) => a[0] - b[0]); // MinHeap based on distance\n  pq.enqueue([0, n]); // [distance, node]\n\n  while (!pq.isEmpty()) {\n    const [d, u] = pq.dequeue();\n    if (d > dist[u]) continue;\n\n    for (const [v, weight] of graph[u]) {\n      if (dist[u] + weight < dist[v]) {\n        dist[v] = dist[u] + weight;\n        pq.enqueue([dist[v], v]);\n      }\n    }\n  }\n\n  // Step 2: DFS with memoization to count restricted paths\n  const memo = Array(n + 1).fill(-1);\n\n  const dfs = (node) => {\n    if (node === n) return 1;\n    if (memo[node] !== -1) return memo[node];\n\n    let paths = 0;\n    for (const [neighbor] of graph[node]) {\n      if (dist[node] > dist[neighbor]) {\n        paths = (paths + dfs(neighbor)) % MOD;\n      }\n    }\n\n    return memo[node] = paths\n  };\n\n  return dfs(1);\n};\n```
['Dynamic Programming', 'Graph', 'Topological Sort', 'Heap (Priority Queue)', 'Shortest Path', 'JavaScript']