Given two positive integers `left` and `right`, find the two integers `num1`
and `num2` such that:

* `left <= num1 < num2 <= right `.
* `num1` and `num2` are both prime numbers.
* `num2 - num1` is the minimum amongst all other pairs satisfying the above conditions.

Return the positive integer array `ans = [num1, num2]`. If there are multiple
pairs satisfying these conditions, return the one with the minimum `num1`
value or `[-1, -1]` if such numbers do not exist.

A number greater than `1` is called prime if it is only divisible by `1` and
itself.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def closestPrimes(self, left: int, right: int) -> List[int]:
    isPrime = self._sieveEratosthenes(right + 1)
    primes=[]
    for i in range(left, right+1):
      if isPrime[i]:
        primes.append(i)

    if len(primes) < 2:
      return [-1, -1]

    minDiff = math.inf
    num1 = -1
    num2 = -1

    for a, b in zip(primes, primes[1:]):
      diff = b - a
      if diff < minDiff:
        minDiff = diff
        num1 = a
        num2 = b

    return [num1, num2]

  def _sieveEratosthenes(self, n: int) -> List[bool]:
    isPrime = [True] * n
    isPrime[0] = False
    isPrime[1] = False
    for i in range(2, int(n**0.5) + 1):
      if isPrime[i]:
        for j in range(i * i, n, i):
          isPrime[j] = False
    return isPrime

class Solution {
  public int[] closestPrimes(int left, int right) {
    final boolean[] isPrime = sieveEratosthenes(right + 1);
    List<Integer> primes = new ArrayList<>();

    for (int i = left; i <= right; ++i)
      if (isPrime[i])
        primes.add(i);

    if (primes.size() < 2)
      return new int[] {-1, -1};

    int minDiff = Integer.MAX_VALUE;
    int num1 = -1;
    int num2 = -1;

    for (int i = 1; i < primes.size(); ++i) {
      final int diff = primes.get(i) - primes.get(i - 1);
      if (diff < minDiff) {
        minDiff = diff;
        num1 = primes.get(i - 1);
        num2 = primes.get(i);
      }
    }

    return new int[] {num1, num2};
  }

  private boolean[] sieveEratosthenes(int n) {
    boolean[] isPrime = new boolean[n];
    Arrays.fill(isPrime, true);
    isPrime[0] = false;
    isPrime[1] = false;
    for (int i = 2; i * i < n; ++i)
      if (isPrime[i])
        for (int j = i * i; j < n; j += i)
          isPrime[j] = false;
    return isPrime;
  }
}

class Solution {
 public:
  vector<int> closestPrimes(int left, int right) {
    const vector<bool> isPrime = sieveEratosthenes(right + 1);
    vector<int> primes;

    for (int i = left; i <= right; ++i)
      if (isPrime[i])
        primes.push_back(i);

    if (primes.size() < 2)
      return {-1, -1};

    int minDiff = INT_MAX;
    int num1 = -1;
    int num2 = -1;

    for (int i = 1; i < primes.size(); ++i) {
      const int diff = primes[i] - primes[i - 1];
      if (diff < minDiff) {
        minDiff = diff;
        num1 = primes[i - 1];
        num2 = primes[i];
      }
    }

    return {num1, num2};
  }

 private:
  vector<bool> sieveEratosthenes(int n) {
    vector<bool> isPrime(n, true);
    isPrime[0] = false;
    isPrime[1] = false;
    for (int i = 2; i * i < n; ++i)
      if (isPrime[i])
        for (int j = i * i; j < n; j += i)
          isPrime[j] = false;
    return isPrime;
  }
};


There are `k` workers who want to move `n` boxes from an old warehouse to a
new one. You are given the two integers `n` and `k`, and a 2D integer array
`time` of size `k x 4` where `time[i] = [leftToRighti, pickOldi, rightToLefti,
putNewi]`.

The warehouses are separated by a river and connected by a bridge. The old
warehouse is on the right bank of the river, and the new warehouse is on the
left bank of the river. Initially, all `k` workers are waiting on the left
side of the bridge. To move the boxes, the `ith` worker (0-indexed) can :

* Cross the bridge from the left bank (new warehouse) to the right bank (old warehouse) in `leftToRighti` minutes.
* Pick a box from the old warehouse and return to the bridge in `pickOldi` minutes. Different workers can pick up their boxes simultaneously.
* Cross the bridge from the right bank (old warehouse) to the left bank (new warehouse) in `rightToLefti` minutes.
* Put the box in the new warehouse and return to the bridge in `putNewi` minutes. Different workers can put their boxes simultaneously.

A worker `i` is less efficient than a worker `j` if either condition is met:

* `leftToRighti + rightToLefti > leftToRightj + rightToLeftj`
* `leftToRighti + rightToLefti == leftToRightj + rightToLeftj` and `i > j`

The following rules regulate the movement of the workers through the bridge :

* If a worker `x` reaches the bridge while another worker `y` is crossing the bridge, `x` waits at their side of the bridge.
* If the bridge is free, the worker waiting on the right side of the bridge gets to cross the bridge. If more than one worker is waiting on the right side, the one with the lowest efficiency crosses first.
* If the bridge is free and no worker is waiting on the right side, and at least one box remains at the old warehouse, the worker on the left side of the river gets to cross the bridge. If more than one worker is waiting on the left side, the one with the lowest efficiency crosses first.

Return the instance of time at which the last worker reaches the left bank of
the river after all n boxes have been put in the new warehouse.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def findCrossingTime(self, n: int, k: int, time: List[List[int]]) -> int:
    ans = 0
    leftBridgeQueue = [(-leftToRight - rightToLeft, -i) for i, (leftToRight, pickOld, rightToLeft, pickNew) in enumerate(time)]
    rightBridgeQueue = []
    leftWorkers = []
    rightWorkers = []

    heapq.heapify(leftBridgeQueue)

    while n > 0 or rightBridgeQueue or rightWorkers:
      while leftWorkers and leftWorkers[0][0] <= ans:
        i = heapq.heappop(leftWorkers)[1]
        heapq.heappush(leftBridgeQueue, (-time[i][0] - time[i][2], -i))
      while rightWorkers and rightWorkers[0][0] <= ans:
        i = heapq.heappop(rightWorkers)[1]
        heapq.heappush(rightBridgeQueue, (-time[i][0] - time[i][2], -i))
      if rightBridgeQueue:
        i = -heapq.heappop(rightBridgeQueue)[1]
        ans += time[i][2]
        heapq.heappush(leftWorkers, (ans + time[i][3], i))
      elif leftBridgeQueue and n > 0:
        i = -heapq.heappop(leftBridgeQueue)[1]
        ans += time[i][0]
        heapq.heappush(rightWorkers, (ans + time[i][1], i))
        n -= 1
      else:
        if leftWorkers and n > 0:
          ans1=leftWorkers[0][0]
        else:
          ans1=math.inf
        if rightWorkers:
          ans2=rightWorkers[0][0]
        else:
          ans2=math.inf
        ans=min(ans1,ans2)

    return ans

class Solution {
  public int findCrossingTime(int n, int k, int[][] time) {
    int ans = 0;
    // (leftToRight + rightToLeft, i)
    Queue<Pair<Integer, Integer>> leftBridgeQueue = createMaxHeap();
    Queue<Pair<Integer, Integer>> rightBridgeQueue = createMaxHeap();
    // (time to be idle, i)
    Queue<Pair<Integer, Integer>> leftWorkers = createMinHeap();
    Queue<Pair<Integer, Integer>> rightWorkers = createMinHeap();

    for (int i = 0; i < k; ++i)
      leftBridgeQueue.offer(new Pair<>(
          /*leftToRight*/ time[i][0] + /*rightToLeft*/ time[i][2], i));

    while (n > 0 || !rightBridgeQueue.isEmpty() || !rightWorkers.isEmpty()) {
      // Idle left workers get on the left bridge.
      while (!leftWorkers.isEmpty() && leftWorkers.peek().getKey() <= ans) {
        final int i = leftWorkers.poll().getValue();
        leftBridgeQueue.offer(new Pair<>(
            /*leftToRight*/ time[i][0] + /*rightToLeft*/ time[i][2], i));
      }
      // Idle right workers get on the right bridge.
      while (!rightWorkers.isEmpty() && rightWorkers.peek().getKey() <= ans) {
        final int i = rightWorkers.poll().getValue();
        rightBridgeQueue.offer(new Pair<>(
            /*leftToRight*/ time[i][0] + /*rightToLeft*/ time[i][2], i));
      }

      if (!rightBridgeQueue.isEmpty()) {
        // If the bridge is free, the worker waiting on the right side of the
        // bridge gets to cross the bridge. If more than one worker is waiting
        // on the right side, the one with the lowest efficiency crosses first.
        final int i = rightBridgeQueue.poll().getValue();
        ans += /*rightToLeft*/ time[i][2];
        leftWorkers.offer(new Pair<>(ans + /*putNew*/ time[i][3], i));
      } else if (!leftBridgeQueue.isEmpty() && n > 0) {
        // If the bridge is free and no worker is waiting on the right side, and
        // at least one box remains at the old warehouse, the worker on the left
        // side of the river gets to cross the bridge. If more than one worker
        // is waiting on the left side, the one with the lowest efficiency
        // crosses first.
        final int i = leftBridgeQueue.poll().getValue();
        ans += /*leftToRight*/ time[i][0];
        rightWorkers.offer(new Pair<>(ans + /*pickOld*/ time[i][1], i));
        --n;
      } else {
        // Advance the time of the last crossing worker.
        ans = Math.min(!leftWorkers.isEmpty() && n > 0 ? leftWorkers.peek().getKey()
                                                       : Integer.MAX_VALUE,
                       !rightWorkers.isEmpty() ? rightWorkers.peek().getKey() : Integer.MAX_VALUE);
      }
    }

    return ans;
  }

  private Queue<Pair<Integer, Integer>> createMaxHeap() {
    return new PriorityQueue<>(
        Comparator.comparing(Pair<Integer, Integer>::getKey, Comparator.reverseOrder())
            .thenComparing(Pair<Integer, Integer>::getValue, Comparator.reverseOrder()));
  }

  private Queue<Pair<Integer, Integer>> createMinHeap() {
    return new PriorityQueue<>(Comparator.comparingInt(Pair<Integer, Integer>::getKey)
                                   .thenComparingInt(Pair<Integer, Integer>::getValue));
  }
}

class Solution {
 public:
  int findCrossingTime(int n, int k, vector<vector<int>>& time) {
    int ans = 0;
    using P = pair<int, int>;
    // (leftToRight + rightToLeft, i)
    priority_queue<P> leftBridgeQueue;
    priority_queue<P> rightBridgeQueue;
    // (time to be idle, i)
    priority_queue<P, vector<P>, greater<>> leftWorkers;
    priority_queue<P, vector<P>, greater<>> rightWorkers;

    for (int i = 0; i < k; ++i)
      leftBridgeQueue.emplace(
          /*leftToRight*/ time[i][0] + /*rightToLeft*/ time[i][2], i);

    while (n > 0 || !rightBridgeQueue.empty() || !rightWorkers.empty()) {
      // Idle left workers get on the left bridge.
      while (!leftWorkers.empty() && leftWorkers.top().first <= ans) {
        const int i = leftWorkers.top().second;
        leftWorkers.pop();
        leftBridgeQueue.emplace(
            /*leftToRight*/ time[i][0] + /*rightToLeft*/ time[i][2], i);
      }
      // Idle right workers get on the right bridge.
      while (!rightWorkers.empty() && rightWorkers.top().first <= ans) {
        const int i = rightWorkers.top().second;
        rightWorkers.pop();
        rightBridgeQueue.emplace(
            /*leftToRight*/ time[i][0] + /*rightToLeft*/ time[i][2], i);
      }

      if (!rightBridgeQueue.empty()) {
        // If the bridge is free, the worker waiting on the right side of the
        // bridge gets to cross the bridge. If more than one worker is waiting
        // on the right side, the one with the lowest efficiency crosses first.
        const int i = rightBridgeQueue.top().second;
        rightBridgeQueue.pop();
        ans += /*rightToLeft*/ time[i][2];
        leftWorkers.emplace(ans + /*putNew*/ time[i][3], i);
      } else if (!leftBridgeQueue.empty() && n > 0) {
        // If the bridge is free and no worker is waiting on the right side, and
        // at least one box remains at the old warehouse, the worker on the left
        // side of the river gets to cross the bridge. If more than one worker
        // is waiting on the left side, the one with the lowest efficiency
        // crosses first.
        const int i = leftBridgeQueue.top().second;
        leftBridgeQueue.pop();
        ans += /*leftToRight*/ time[i][0];
        rightWorkers.emplace(ans + /*pickOld*/ time[i][1], i);
        --n;
      } else {
        // Advance the time of the last crossing worker.
        ans = min(
            !leftWorkers.empty() && n > 0 ? leftWorkers.top().first : INT_MAX,
            !rightWorkers.empty() ? rightWorkers.top().first : INT_MAX);
      }
    }

    return ans;
  }
};


You are given a `m x n` matrix `grid` consisting of non-negative integers
where `grid[row][col]` represents the minimum time required to be able to
visit the cell `(row, col)`, which means you can visit the cell `(row, col)`
only when the time you visit it is greater than or equal to `grid[row][col]`.

You are standing in the top-left cell of the matrix in the `0th` second, and
you must move to any adjacent cell in the four directions: up, down, left, and
right. Each move you make takes 1 second.

Return the minimum time required in which you can visit the bottom-right cell
of the matrix. If you cannot visit the bottom-right cell, then return `-1`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def minimumTime(self, grid: List[List[int]]) -> int:
    if grid[0][1] > 1 and grid[1][0] > 1:
      return -1

    dirs = ((0, 1), (1, 0), (0, -1), (-1, 0))
    m = len(grid)
    n = len(grid[0])
    minHeap = [(0, 0, 0)]
    seen = {(0, 0)}

    while minHeap:
      time, i, j = heapq.heappop(minHeap)
      if i == m - 1 and j == n - 1:
        return time
      for dx, dy in dirs:
        x = i + dx
        y = j + dy
        if x < 0 or x == m or y < 0 or y == n:
          continue
        if (x, y) in seen:
          continue
        if (grid[x][y] - time) % 2 == 0:
          extraWait = 1
        else:
          extraWait = 0
        nextTime = max(time + 1, grid[x][y] + extraWait)
        heapq.heappush(minHeap, (nextTime, x, y))
        seen.add((x, y))

class Solution {
  public int minimumTime(int[][] grid) {
    if (grid[0][1] > 1 && grid[1][0] > 1)
      return -1;

    final int[][] DIRS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    final int m = grid.length;
    final int n = grid[0].length;
    Queue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])) {
      { offer(new int[] {0, 0, 0}); } // (time, i, j)
    };
    boolean[][] seen = new boolean[m][n];
    seen[0][0] = true;

    while (!minHeap.isEmpty()) {
      final int time = minHeap.peek()[0];
      final int i = minHeap.peek()[1];
      final int j = minHeap.poll()[2];
      if (i == m - 1 && j == n - 1)
        return time;
      for (int[] dir : DIRS) {
        final int x = i + dir[0];
        final int y = j + dir[1];
        if (x < 0 || x == m || y < 0 || y == n)
          continue;
        if (seen[x][y])
          continue;
        final int extraWait = (grid[x][y] - time) % 2 == 0 ? 1 : 0;
        final int nextTime = Math.max(time + 1, grid[x][y] + extraWait);
        minHeap.offer(new int[] {nextTime, x, y});
        seen[x][y] = true;
      }
    }

    throw new IllegalArgumentException();
  }
}

class Solution {
 public:
  int minimumTime(vector<vector<int>>& grid) {
    if (grid[0][1] > 1 && grid[1][0] > 1)
      return -1;

    constexpr int kDirs[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    const int m = grid.size();
    const int n = grid[0].size();
    using T = tuple<int, int, int>;  // (time, i, j)
    priority_queue<T, vector<T>, greater<>> minHeap;
    vector<vector<bool>> seen(m, vector<bool>(n));

    minHeap.emplace(0, 0, 0);
    seen[0][0] = true;

    while (!minHeap.empty()) {
      const auto [time, i, j] = minHeap.top();
      minHeap.pop();
      if (i == m - 1 && j == n - 1)
        return time;
      for (const auto& [dx, dy] : kDirs) {
        const int x = i + dx;
        const int y = j + dy;
        if (x < 0 || x == m || y < 0 || y == n)
          continue;
        if (seen[x][y])
          continue;
        const int extraWait = (grid[x][y] - time) % 2 == 0 ? 1 : 0;
        const int nextTime = max(time + 1, grid[x][y] + extraWait);
        minHeap.emplace(nextTime, x, y);
        seen[x][y] = true;
      }
    }

    throw;
  }
};


You are given a 0-indexed integer array `nums` of length `n`.

You can perform the following operation as many times as you want:

* Pick an index `i` that you haven’t picked before, and pick a prime `p` strictly less than `nums[i]`, then subtract `p` from `nums[i]`.

Return true if you can make `nums` a strictly increasing array using the above
operation and false otherwise.

A strictly increasing array is an array whose each element is strictly greater
than its preceding element.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def primeSubOperation(self, nums: List[int]) -> bool:
    kMax = 1000
    primes = self._sieveEratosthenes(kMax)

    prevNum = 0
    for num in nums:
      i = bisect.bisect_left(primes, num - prevNum)
      if i > 0:
        num -= primes[i - 1]
      if num <= prevNum:
        return False
      prevNum = num

    return True

  def _sieveEratosthenes(self, n: int) -> List[int]:
    isPrime = [True] * n
    isPrime[0] = False
    isPrime[1] = False
    for i in range(2, int(n**0.5) + 1):
      if isPrime[i]:
        for j in range(i * i, n, i):
          isPrime[j] = False
    return [i for i in range(n) if isPrime[i]]

class Solution {
  public boolean primeSubOperation(int[] nums) {
    final int MAX = 1000;
    final List<Integer> primes = sieveEratosthenes(MAX);

    int prevNum = 0;
    for (int num : nums) {
      // Make nums[i] the smallest as possible and still > nums[i - 1].
      final int i = firstGreaterEqual(primes, num - prevNum);
      if (i > 0)
        num -= primes.get(i - 1);
      if (num <= prevNum)
        return false;
      prevNum = num;
    }

    return true;
  }

  private List<Integer> sieveEratosthenes(int n) {
    List<Integer> primes = new ArrayList<>();
    boolean[] isPrime = new boolean[n];
    Arrays.fill(isPrime, true);
    isPrime[0] = false;
    isPrime[1] = false;
    for (int i = 2; i * i < n; ++i)
      if (isPrime[i])
        for (int j = i * i; j < n; j += i)
          isPrime[j] = false;
    for (int i = 2; i < n; ++i)
      if (isPrime[i])
        primes.add(i);
    return primes;
  }

  private int firstGreaterEqual(List<Integer> A, int target) {
    final int i = Collections.binarySearch(A, target);
    return i < 0 ? -i - 1 : i;
  }
}

class Solution {
 public:
  bool primeSubOperation(vector<int>& nums) {
    constexpr int kMax = 1000;
    const vector<int> primes = sieveEratosthenes(kMax);

    int prevNum = 0;
    for (int num : nums) {
      // Make nums[i] the smallest as possible and still > nums[i - 1].
      const auto it = ranges::lower_bound(primes, num - prevNum);
      if (it != primes.begin())
        num -= *prev(it);
      if (num <= prevNum)
        return false;
      prevNum = num;
    }

    return true;
  }

  vector<int> sieveEratosthenes(int n) {
    vector<int> primes;
    vector<bool> isPrime(n, true);
    isPrime[0] = false;
    isPrime[1] = false;
    for (int i = 2; i * i < n; ++i)
      if (isPrime[i])
        for (int j = i * i; j < n; j += i)
          isPrime[j] = false;
    for (int i = 2; i < n; ++i)
      if (isPrime[i])
        primes.push_back(i);
    return primes;
  }
};


There exists an undirected and unrooted tree with `n` nodes indexed from `0`
to `n - 1`. You are given an integer `n` and a 2D integer array edges of
length `n - 1`, where `edges[i] = [ai, bi]` indicates that there is an edge
between nodes `ai` and `bi` in the tree. You are also given an array `coins`
of size `n` where `coins[i]` can be either `0` or `1`, where `1` indicates the
presence of a coin in the vertex `i`.

Initially, you choose to start at any vertex in the tree. Then, you can
perform the following operations any number of times:

* Collect all the coins that are at a distance of at most `2` from the current vertex, or
* Move to any adjacent vertex in the tree.

Find the minimum number of edges you need to go through to collect all the
coins and go back to the initial vertex.

Note that if you pass an edge several times, you need to count it into the
answer several times.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def collectTheCoins(self, coins: List[int], edges: List[List[int]]) -> int:
    n = len(coins)
    tree = [set() for _ in range(n)]
    leavesToBeRemoved = collections.deque()

    for u, v in edges:
      tree[u].add(v)
      tree[v].add(u)

    for u in range(n):
      while len(tree[u]) == 1 and coins[u] == 0:
        v = tree[u].pop()
        tree[v].remove(u)
        u = v
      if len(tree[u]) == 1:
        leavesToBeRemoved.append(u)

    for _ in range(2):
      for _ in range(len(leavesToBeRemoved)):
        u = leavesToBeRemoved.popleft()
        if tree[u]:
          v = tree[u].pop()
          tree[v].remove(u)
          if len(tree[v]) == 1:
            leavesToBeRemoved.append(v)

    return sum(len(children) for children in tree)

class Solution {
  public int collectTheCoins(int[] coins, int[][] edges) {
    final int n = coins.length;
    Set<Integer>[] tree = new Set[n];
    Deque<Integer> leavesToBeRemoved = new ArrayDeque<>();

    for (int i = 0; i < n; ++i)
      tree[i] = new HashSet<>();

    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      tree[u].add(v);
      tree[v].add(u);
    }

    for (int i = 0; i < n; ++i) {
      int u = i;
      // Remove the leaves that don't have coins.
      while (tree[u].size() == 1 && coins[u] == 0) {
        final int v = tree[u].iterator().next();
        tree[u].clear();
        tree[v].remove(u);
        u = v; // Walk up to its parent.
      }
      // After trimming leaves without coins, leaves with coins may satisfy
      // `leavesToBeRemoved`.
      if (tree[u].size() == 1)
        leavesToBeRemoved.offer(u);
    }

    // Remove each remaining leaf node and its parent. The remaining nodes are
    // the ones that must be visited.
    for (int i = 0; i < 2; ++i)
      for (int sz = leavesToBeRemoved.size(); sz > 0; --sz) {
        final int u = leavesToBeRemoved.poll();
        if (!tree[u].isEmpty()) {
          final int v = tree[u].iterator().next();
          tree[u].clear();
          tree[v].remove(u);
          if (tree[v].size() == 1)
            leavesToBeRemoved.offer(v);
        }
      }

    return Arrays.stream(tree).mapToInt(children -> children.size()).sum();
  }
}

class Solution {
 public:
  int collectTheCoins(vector<int>& coins, vector<vector<int>>& edges) {
    const int n = coins.size();
    vector<unordered_set<int>> tree(n);
    queue<int> leavesToBeRemoved;

    for (const vector<int>& edge : edges) {
      const int u = edge[0];
      const int v = edge[1];
      tree[u].insert(v);
      tree[v].insert(u);
    }

    for (int i = 0; i < n; ++i) {
      int u = i;
      // Remove the leaves that don't have coins.
      while (tree[u].size() == 1 && coins[u] == 0) {
        const int v = *tree[u].begin();
        tree[u].clear();
        tree[v].erase(u);
        u = v;  // Walk up to its parent.
      }
      // After trimming leaves without coins, leaves with coins may satisfy
      // `leavesToBeRemoved`.
      if (tree[u].size() == 1)
        leavesToBeRemoved.push(u);
    }

    // Remove each remaining leaf node and its parent. The remaining nodes are
    // the ones that must be visited.
    for (int i = 0; i < 2; ++i)
      for (int sz = leavesToBeRemoved.size(); sz > 0; --sz) {
        const int u = leavesToBeRemoved.front();
        leavesToBeRemoved.pop();
        if (!tree[u].empty()) {
          const int v = *tree[u].begin();
          tree[u].clear();
          tree[v].erase(u);
          if (tree[v].size() == 1)
            leavesToBeRemoved.push(v);
        }
      }

    return accumulate(tree.begin(), tree.end(), 0,
                      [](int acc, const unordered_set<int>& children) {
      return acc + children.size();
    });
  }
};


Given an integer array `nums` containing `n` integers, find the beauty of each
subarray of size `k`.

The beauty of a subarray is the `xth` smallest integer in the subarray if it
is negative, or `0` if there are fewer than `x` negative integers.

Return an integer array containing `n - k + 1` integers, which denote the
beauty of the subarrays in order from the first index in the array.

* A subarray is a contiguous non-empty sequence of elements within an array.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]:
    ans = []
    count = [0] * 50

    for i, num in enumerate(nums):
      if num < 0:
        count[num + 50] += 1
      if i - k >= 0 and nums[i - k] < 0:
        count[nums[i - k] + 50] -= 1
      if i + 1 >= k:
        ans.append(self._getXthSmallestNum(count, x))

    return ans

  def _getXthSmallestNum(self, count: List[int], x: int) -> int:
    prefix = 0
    for i in range(50):
      prefix += count[i]
      if prefix >= x:
        return i - 50
    return 0

class Solution {
  public int[] getSubarrayBeauty(int[] nums, int k, int x) {
    int[] ans = new int[nums.length - k + 1];
    int[] count = new int[50]; // count[i] := the frequency of (i + 50)

    for (int i = 0; i < nums.length; ++i) {
      if (nums[i] < 0)
        ++count[nums[i] + 50];
      if (i - k >= 0 && nums[i - k] < 0)
        --count[nums[i - k] + 50];
      if (i + 1 >= k)
        ans[i - k + 1] = getXthSmallestNum(count, x);
    }

    return ans;
  }

  private int getXthSmallestNum(int[] count, int x) {
    int prefix = 0;
    for (int i = 0; i < 50; ++i) {
      prefix += count[i];
      if (prefix >= x)
        return i - 50;
    }
    return 0;
  }
}

class Solution {
 public:
  vector<int> getSubarrayBeauty(vector<int>& nums, int k, int x) {
    vector<int> ans;
    vector<int> count(50);  // count[i] := the frequency of (i + 50)

    for (int i = 0; i < nums.size(); ++i) {
      if (nums[i] < 0)
        ++count[nums[i] + 50];
      if (i - k >= 0 && nums[i - k] < 0)
        --count[nums[i - k] + 50];
      if (i + 1 >= k)
        ans.push_back(getXthSmallestNum(count, x));
    }

    return ans;
  }

 private:
  int getXthSmallestNum(const vector<int>& count, int x) {
    int prefix = 0;
    for (int i = 0; i < 50; ++i) {
      prefix += count[i];
      if (prefix >= x)
        return i - 50;
    }
    return 0;
  }
};


Minimum Cost of a Path With Special Roads

You are given an array `start` where `start = [startX, startY]` represents
your initial position `(startX, startY)` in a 2D space. You are also given the
array `target` where `target = [targetX, targetY]` represents your target
position `(targetX, targetY)`.

The cost of going from a position `(x1, y1)` to any other position in the
space `(x2, y2)` is `|x2 - x1| + |y2 - y1|`.

There are also some special roads. You are given a 2D array `specialRoads`
where `specialRoads[i] = [x1i, y1i, x2i, y2i, costi]` indicates that the `ith`
special road can take you from `(x1i, y1i)` to `(x2i, y2i)` with a cost equal
to `costi`. You can use each special road any number of times.

Return the minimum cost required to go from `(startX, startY)` to `(targetX,
targetY)`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def minimumCost(self, start: List[int], target: List[int], specialRoads: List[List[int]]) -> int:
    return self.dijkstra(specialRoads, *start, *target)

  def dijkstra(self, specialRoads: List[List[int]], srcX: int, srcY: int, dstX: int, dstY: int) -> int:
    n = len(specialRoads)
    dist = [math.inf] * n
    minHeap = []

    for u, (x1, y1, _, _, cost) in enumerate(specialRoads):
      d = abs(x1 - srcX) + abs(y1 - srcY) + cost
      dist[u] = d
      heapq.heappush(minHeap, (dist[u], u))

    while minHeap:
      d, u = heapq.heappop(minHeap)
      if d > dist[u]:
        continue
      _, _, ux2, uy2, _ = specialRoads[u]
      for v in range(n):
        if v == u:
          continue
        vx1, vy1, _, _, vcost = specialRoads[v]
        newDist = d + abs(vx1 - ux2) + abs(vy1 - uy2) + vcost
        if newDist < dist[v]:
          dist[v] = newDist
          heapq.heappush(minHeap, (dist[v], v))

    ans = abs(dstX - srcX) + abs(dstY - srcY)
    for u in range(n):
      _, _, x2, y2, _ = specialRoads[u]
      ans = min(ans, dist[u] + abs(dstX - x2) + abs(dstY - y2))

    return ans

class Solution {
  public int minimumCost(int[] start, int[] target, int[][] specialRoads) {
    return dijkstra(specialRoads, start[0], start[1], target[0], target[1]);
  }

  private int dijkstra(int[][] specialRoads, int srcX, int srcY, int dstX, int dstY) {
    final int n = specialRoads.length;
    // dist[i] := the minimum distance of (srcX, srcY) to
    // specialRoads[i](x2, y2)
    int[] dist = new int[n];
    Arrays.fill(dist, Integer.MAX_VALUE);
    // (d, u), where u := the i-th specialRoads
    Queue<Pair<Integer, Integer>> minHeap =
        new PriorityQueue<>(Comparator.comparingInt(Pair::getKey));

    // (srcX, srcY) -> (x1, y1) to cost -> (x2, y2)
    for (int u = 0; u < n; ++u) {
      final int x1 = specialRoads[u][0];
      final int y1 = specialRoads[u][1];
      final int cost = specialRoads[u][4];
      final int d = Math.abs(x1 - srcX) + Math.abs(y1 - srcY) + cost;
      dist[u] = d;
      minHeap.offer(new Pair<>(dist[u], u));
    }

    while (!minHeap.isEmpty()) {
      final int d = minHeap.peek().getKey();
      final int u = minHeap.poll().getValue();
      if (d > dist[u])
        continue;
      final int ux2 = specialRoads[u][2];
      final int uy2 = specialRoads[u][3];
      for (int v = 0; v < n; ++v) {
        if (v == u)
          continue;
        final int vx1 = specialRoads[v][0];
        final int vy1 = specialRoads[v][1];
        final int vcost = specialRoads[v][4];
        // (ux2, uy2) -> (vx1, vy1) to vcost -> (vx2, vy2)
        final int newDist = d + Math.abs(vx1 - ux2) + Math.abs(vy1 - uy2) + vcost;
        if (newDist < dist[v]) {
          dist[v] = newDist;
          minHeap.offer(new Pair<>(dist[v], v));
        }
      }
    }

    int ans = Math.abs(dstX - srcX) + Math.abs(dstY - srcY);
    for (int u = 0; u < n; ++u) {
      final int x2 = specialRoads[u][2];
      final int y2 = specialRoads[u][3];
      // (srcX, srcY) -> (x2, y2) -> (dstX, dstY).
      ans = Math.min(ans, dist[u] + Math.abs(dstX - x2) + Math.abs(dstY - y2));
    }
    return ans;
  }
}

class Solution {
 public:
  int minimumCost(vector<int>& start, vector<int>& target,
                  vector<vector<int>>& specialRoads) {
    return dijkstra(specialRoads, start[0], start[1], target[0], target[1]);
  }

 private:
  int dijkstra(const vector<vector<int>>& specialRoads, int srcX, int srcY,
               int dstX, int dstY) {
    const int n = specialRoads.size();
    // dist[i] := the minimum distance of (srcX, srcY) to
    // specialRoads[i](x2, y2)
    vector<int> dist(specialRoads.size(), INT_MAX);
    using P = pair<int, int>;  // (d, u), where u := the i-th specialRoads
    priority_queue<P, vector<P>, greater<>> minHeap;

    // (srcX, srcY) -> (x1, y1) to cost -> (x2, y2)
    for (int u = 0; u < n; ++u) {
      const int x1 = specialRoads[u][0];
      const int y1 = specialRoads[u][1];
      const int cost = specialRoads[u][4];
      const int d = abs(x1 - srcX) + abs(y1 - srcY) + cost;
      dist[u] = d;
      minHeap.emplace(dist[u], u);
    }

    while (!minHeap.empty()) {
      const auto [d, u] = minHeap.top();
      minHeap.pop();
      if (d > dist[u])
        continue;
      const int ux2 = specialRoads[u][2];
      const int uy2 = specialRoads[u][3];
      for (int v = 0; v < n; ++v) {
        if (v == u)
          continue;
        const int vx1 = specialRoads[v][0];
        const int vy1 = specialRoads[v][1];
        const int vcost = specialRoads[v][4];
        // (ux2, uy2) -> (vx1, vy1) to vcost -> (vx2, vy2)
        const int newDist = d + abs(vx1 - ux2) + abs(vy1 - uy2) + vcost;
        if (newDist < dist[v]) {
          dist[v] = newDist;
          minHeap.emplace(dist[v], v);
        }
      }
    }

    int ans = abs(dstX - srcX) + abs(dstY - srcY);
    for (int u = 0; u < n; ++u) {
      const int x2 = specialRoads[u][2];
      const int y2 = specialRoads[u][3];
      // (srcX, srcY) -> (x2, y2) -> (dstX, dstY).
      ans = min(ans, dist[u] + abs(dstX - x2) + abs(dstY - y2));
    }
    return ans;
  }
};


Lexicographically Smallest Beautiful String

A string is beautiful if:

* It consists of the first `k` letters of the English lowercase alphabet.
* It does not contain any substring of length `2` or more which is a palindrome.

You are given a beautiful string `s` of length `n` and a positive integer `k`.

Return the lexicographically smallest string of length `n`, which is larger
than `s` and is beautiful. If there is no such string, return an empty string.

A string `a` is lexicographically larger than a string `b` (of the same
length) if in the first position where `a` and `b` differ, `a` has a character
strictly larger than the corresponding character in `b`.

* For example, `"abcd"` is lexicographically larger than `"abcc"` because the first position they differ is at the fourth character, and `d` is greater than `c`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def smallestBeautifulString(self, s: str, k: int) -> str:
    chars = list(s)

    for i in reversed(range(len(chars))):
      chars[i] = chr(ord(chars[i]) + 1)
      while self._containsPalindrome(chars, i):
        chars[i] = chr(ord(chars[i]) + 1)
      if chars[i] < chr(ord('a') + k):
        return self._changeSuffix(chars, i + 1)

    return ''

  def _containsPalindrome(self, chars: List[str], i: int) -> bool:
    return (i > 0 and chars[i] == chars[i - 1]) or (i > 1 and chars[i] == chars[i - 2])

  def _changeSuffix(self, chars: List[str], i: int) -> str:
    for j in range(i, len(chars)):
      chars[j] = 'a'
      while self._containsPalindrome(chars, j):
        chars[j] = chr(ord(chars[j]) + 1)
    return ''.join(chars)

class Solution {
  public String smallestBeautifulString(String s, int k) {
    StringBuilder sb = new StringBuilder(s);

    for (int i = s.length() - 1; i >= 0; --i) {
      do {
        sb.setCharAt(i, (char) (sb.charAt(i) + 1));
      } while (containsPalindrome(sb, i));
      if (sb.charAt(i) < 'a' + k)
        // If sb[i] is among the first k letters, then change the letters after
        // sb[i] to the smallest ones that don't form any palindrome substring.
        return changeSuffix(sb, i + 1);
    }

    return "";
  }

  // Returns true if sb[0..i] contains any palindrome.
  private boolean containsPalindrome(StringBuilder sb, int i) {
    return (i > 0 && sb.charAt(i) == sb.charAt(i - 1)) ||
        (i > 1 && sb.charAt(i) == sb.charAt(i - 2));
  }

  // Returns a string, where replacing sb[i..n) with the smallest possible
  // letters don't form any palindrome substring.
  private String changeSuffix(StringBuilder sb, int i) {
    for (int j = i; j < sb.length(); ++j)
      for (sb.setCharAt(j, 'a'); containsPalindrome(sb, j);
           sb.setCharAt(j, (char) (sb.charAt(j) + 1)))
        ;
    return sb.toString();
  }
}

class Solution {
 public:
  string smallestBeautifulString(string s, int k) {
    for (int i = s.length() - 1; i >= 0; --i) {
      do {
        ++s[i];
      } while (containsPalindrome(s, i));
      if (s[i] < 'a' + k)
        // If s[i] is among the first k letters, then change the letters after
        // s[i] to the smallest ones that don't form any palindrome substring.
        return changeSuffix(s, i + 1);
    }

    return "";
  }

 private:
  // Returns true if s[0..i] contains any palindrome.
  bool containsPalindrome(const string& s, int i) {
    return (i > 0 && s[i] == s[i - 1]) || (i > 1 && s[i] == s[i - 2]);
  }

  // Returns a string, where replacing s[i..n) with the smallest possible
  // letters don't form any palindrome substring.
  string changeSuffix(string& s, int i) {
    for (int j = i; j < s.length(); ++j)
      for (s[j] = 'a'; containsPalindrome(s, j); ++s[j])
        ;
    return s;
  }
};


Number of Adjacent Elements With the Same Color

There is a 0-indexed array `nums` of length `n`. Initially, all elements are
uncolored (has a value of `0`).

You are given a 2D integer array `queries` where `queries[i] = [indexi,
colori]`.

For each query, you color the index `indexi` with the color `colori` in the
array `nums`.

Return an array `answer` of the same length as `queries` where `answer[i]` is
the number of adjacent elements with the same color after the `ith` query.

More formally, `answer[i]` is the number of indices `j`, such that `0 <= j < n
- 1` and `nums[j] == nums[j + 1]` and `nums[j] != 0` after the `ith` query.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def colorTheArray(self, n: int, queries: List[List[int]]) -> List[int]:
    ans = []
    arr = [0] * n
    sameColors = 0

    for i, color in queries:
      if i + 1 < n:
        if arr[i + 1] > 0 and arr[i + 1] == arr[i]:
          sameColors -= 1
        if arr[i + 1] == color:
          sameColors += 1
      if i > 0:
        if arr[i - 1] > 0 and arr[i - 1] == arr[i]:
          sameColors -= 1
        if arr[i - 1] == color:
          sameColors += 1
      arr[i] = color
      ans.append(sameColors)

    return ans

class Solution {
  public int[] colorTheArray(int n, int[][] queries) {
    int[] ans = new int[queries.length];
    int[] arr = new int[n];
    int sameColors = 0;

    for (int i = 0; i < queries.length; ++i) {
      final int j = queries[i][0];
      final int color = queries[i][1];
      if (j + 1 < n) {
        if (arr[j + 1] > 0 && arr[j + 1] == arr[j])
          --sameColors;
        if (arr[j + 1] == color)
          ++sameColors;
      }
      if (j > 0) {
        if (arr[j - 1] > 0 && arr[j - 1] == arr[j])
          --sameColors;
        if (arr[j - 1] == color)
          ++sameColors;
      }
      arr[j] = color;
      ans[i] = sameColors;
    }

    return ans;
  }
}

class Solution {
 public:
  vector<int> colorTheArray(int n, vector<vector<int>>& queries) {
    vector<int> ans;
    vector<int> arr(n);
    int sameColors = 0;

    for (const vector<int>& query : queries) {
      const int i = query[0];
      const int color = query[1];
      if (i + 1 < n) {
        if (arr[i + 1] > 0 && arr[i + 1] == arr[i])
          --sameColors;
        if (arr[i + 1] == color)
          ++sameColors;
      }
      if (i > 0) {
        if (arr[i - 1] > 0 && arr[i - 1] == arr[i])
          --sameColors;
        if (arr[i - 1] == color)
          ++sameColors;
      }
      arr[i] = color;
      ans.push_back(sameColors);
    }

    return ans;
  }
};


You are given a 0-indexed `m x n` matrix `grid` consisting of positive
integers.

You can start at any cell in the first column of the matrix, and traverse the
grid in the following way:

* From a cell `(row, col)`, you can move to any of the cells: `(row - 1, col + 1)`, `(row, col + 1)` and `(row + 1, col + 1)` such that the value of the cell you move to, should be strictly bigger than the value of the current cell.

Return the maximum number of moves that you can perform.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def maxMoves(self, grid: List[List[int]]) -> int:
    m = len(grid)
    n = len(grid[0])
    dp = [[0] * n for _ in range(m)]

    for j in range(n - 2, -1, -1):
      for i in range(m):
        if grid[i][j + 1] > grid[i][j]:
          dp[i][j] = 1 + dp[i][j + 1]
        if i > 0 and grid[i - 1][j + 1] > grid[i][j]:
          dp[i][j] = max(dp[i][j], 1 + dp[i - 1][j + 1])
        if i + 1 < m and grid[i + 1][j + 1] > grid[i][j]:
          dp[i][j] = max(dp[i][j], 1 + dp[i + 1][j + 1])

    return max(dp[i][0] for i in range(m))

class Solution {
  public int maxMoves(int[][] grid) {
    final int m = grid.length;
    final int n = grid[0].length;
    int ans = 0;
    // dp[i][j] := the maximum number of moves you can perform from (i, j)
    int[][] dp = new int[m][n];

    for (int j = n - 2; j >= 0; --j)
      for (int i = 0; i < m; ++i) {
        if (grid[i][j + 1] > grid[i][j])
          dp[i][j] = 1 + dp[i][j + 1];
        if (i > 0 && grid[i - 1][j + 1] > grid[i][j])
          dp[i][j] = Math.max(dp[i][j], 1 + dp[i - 1][j + 1]);
        if (i + 1 < m && grid[i + 1][j + 1] > grid[i][j])
          dp[i][j] = Math.max(dp[i][j], 1 + dp[i + 1][j + 1]);
      }

    for (int i = 0; i < m; ++i)
      ans = Math.max(ans, dp[i][0]);

    return ans;
  }
}

class Solution {
 public:
  int maxMoves(vector<vector<int>>& grid) {
    const int m = grid.size();
    const int n = grid[0].size();
    int ans = 0;
    // dp[i][j] := the maximum number of moves you can perform from (i, j)
    vector<vector<int>> dp(m, vector<int>(n));

    for (int j = n - 2; j >= 0; --j)
      for (int i = 0; i < m; ++i) {
        if (grid[i][j + 1] > grid[i][j])
          dp[i][j] = 1 + dp[i][j + 1];
        if (i > 0 && grid[i - 1][j + 1] > grid[i][j])
          dp[i][j] = max(dp[i][j], 1 + dp[i - 1][j + 1]);
        if (i + 1 < m && grid[i + 1][j + 1] > grid[i][j])
          dp[i][j] = max(dp[i][j], 1 + dp[i + 1][j + 1]);
      }

    for (int i = 0; i < m; ++i)
      ans = max(ans, dp[i][0]);

    return ans;
  }
};


You are given an integer `n`. There is an undirected graph with `n` vertices,
numbered from `0` to `n - 1`. You are given a 2D integer array `edges` where
`edges[i] = [ai, bi]` denotes that there exists an undirected edge connecting
vertices `ai` and `bi`.

Return the number of complete connected components of the graph.

A connected component is a subgraph of a graph in which there exists a path
between any two vertices, and no vertex of the subgraph shares an edge with a
vertex outside of the subgraph.

A connected component is said to be complete if there exists an edge between
every pair of its vertices.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class UnionFind:
  def __init__(self, n: int):
    self.id = list(range(n))
    self.rank = [0] * n
    self.nodeCount = [1] * n
    self.edgeCount = [0] * n

  def unionByRank(self, u: int, v: int) -> None:
    i = self.find(u)
    j = self.find(v)
    self.edgeCount[i] += 1
    if i == j:
      return
    if self.rank[i] < self.rank[j]:
      self.id[i] = j
      self.edgeCount[j] += self.edgeCount[i]
      self.nodeCount[j] += self.nodeCount[i]
    elif self.rank[i] > self.rank[j]:
      self.id[j] = i
      self.edgeCount[i] += self.edgeCount[j]
      self.nodeCount[i] += self.nodeCount[j]
    else:
      self.id[i] = j
      self.edgeCount[j] += self.edgeCount[i]
      self.nodeCount[j] += self.nodeCount[i]
      self.rank[j] += 1

  def find(self, u: int) -> int:
    if self.id[u] != u:
      self.id[u] = self.find(self.id[u])
    return self.id[u]

  def isComplete(self, u):
    return self.nodeCount[u] * (self.nodeCount[u] - 1) // 2 == self.edgeCount[u]

class Solution:
  def countCompleteComponents(self, n: int, edges: List[List[int]]) -> int:
    ans = 0
    uf = UnionFind(n)
    parents = set()

    for u, v in edges:
      uf.unionByRank(u, v)

    for i in range(n):
      parent = uf.find(i)
      if parent not in parents and uf.isComplete(parent):
        ans += 1
        parents.add(parent)

    return ans

class UnionFind {
  public UnionFind(int n) {
    id = new int[n];
    rank = new int[n];
    nodeCount = new int[n];
    edgeCount = new int[n];
    for (int i = 0; i < n; ++i) {
      id[i] = i;
      nodeCount[i] = 1;
    }
  }

  public void unionByRank(int u, int v) {
    final int i = find(u);
    final int j = find(v);
    ++edgeCount[i];
    if (i == j)
      return;
    if (rank[i] < rank[j]) {
      id[i] = j;
      edgeCount[j] += edgeCount[i];
      nodeCount[j] += nodeCount[i];
    } else if (rank[i] > rank[j]) {
      id[j] = i;
      edgeCount[i] += edgeCount[j];
      nodeCount[i] += nodeCount[j];
    } else {
      id[i] = j;
      edgeCount[j] += edgeCount[i];
      nodeCount[j] += nodeCount[i];
      ++rank[j];
    }
  }

  public int find(int u) {
    return id[u] == u ? u : (id[u] = find(id[u]));
  }

  public boolean isComplete(int u) {
    return nodeCount[u] * (nodeCount[u] - 1) / 2 == edgeCount[u];
  }

  private int[] id;
  private int[] rank;
  private int[] nodeCount;
  private int[] edgeCount;
}

class Solution {
  public int countCompleteComponents(int n, int[][] edges) {
    int ans = 0;
    UnionFind uf = new UnionFind(n);
    Set<Integer> parents = new HashSet<>();

    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      uf.unionByRank(u, v);
    }

    for (int i = 0; i < n; ++i) {
      final int parent = uf.find(i);
      if (parents.add(parent) && uf.isComplete(parent))
        ++ans;
    }

    return ans;
  }
}

class UnionFind {
 public:
  UnionFind(int n) : id(n), rank(n), nodeCount(n, 1), edgeCount(n) {
    iota(id.begin(), id.end(), 0);
  }

  void unionByRank(int u, int v) {
    const int i = find(u);
    const int j = find(v);
    ++edgeCount[i];
    if (i == j)
      return;
    if (rank[i] < rank[j]) {
      id[i] = j;
      edgeCount[j] += edgeCount[i];
      nodeCount[j] += nodeCount[i];
    } else if (rank[i] > rank[j]) {
      id[j] = i;
      edgeCount[i] += edgeCount[j];
      nodeCount[i] += nodeCount[j];
    } else {
      id[i] = j;
      edgeCount[j] += edgeCount[i];
      nodeCount[j] += nodeCount[i];
      ++rank[j];
    }
  }

  int find(int u) {
    return id[u] == u ? u : id[u] = find(id[u]);
  }

  bool isComplete(int u) {
    return nodeCount[u] * (nodeCount[u] - 1) / 2 == edgeCount[u];
  }

 private:
  vector<int> id;
  vector<int> rank;
  vector<int> nodeCount;
  vector<int> edgeCount;
};

class Solution {
 public:
  int countCompleteComponents(int n, vector<vector<int>>& edges) {
    int ans = 0;
    UnionFind uf(n);
    unordered_set<int> parents;

    for (const vector<int>& edge : edges) {
      const int u = edge[0];
      const int v = edge[1];
      uf.unionByRank(u, v);
    }

    for (int i = 0; i < n; ++i) {
      const int parent = uf.find(i);
      if (parents.insert(parent).second && uf.isComplete(parent))
        ++ans;
    }

    return ans;
  }
};


You are given an undirected weighted connected graph containing `n` nodes
labeled from `0` to `n - 1`, and an integer array `edges` where `edges[i] =
[ai, bi, wi]` indicates that there is an edge between nodes `ai` and `bi` with
weight `wi`.

Some edges have a weight of `-1` (`wi = -1`), while others have a positive
weight (`wi > 0`).

Your task is to modify all edges with a weight of `-1` by assigning them
positive integer values in the range `[1, 2 * 109]` so that the shortest
distance between the nodes `source` and `destination` becomes equal to an
integer `target`. If there are multiple modifications that make the shortest
distance between `source` and `destination` equal to `target`, any of them
will be considered correct.

Return an array containing all edges (even unmodified ones) in any order if it
is possible to make the shortest distance from `source` to `destination` equal
to `target`, or an empty array if it's impossible.

Note: You are not allowed to modify the weights of edges with initial positive
weights.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def modifiedGraphEdges(self, n: int, edges: List[List[int]], source: int, destination: int, target: int) -> List[List[int]]:
    kMax = 2_000_000_000
    graph = [[] for _ in range(n)]

    for u, v, w in edges:
      if w == -1:
        continue
      graph[u].append((v, w))
      graph[v].append((u, w))

    distToDestination = self._dijkstra(graph, source, destination)
    if distToDestination < target:
      return []
    if distToDestination == target:
      for edge in edges:
        if edge[2] == -1:
          edge[2] = kMax
      return edges

    for i, (u, v, w) in enumerate(edges):
      if w != -1:
        continue
      edges[i][2] = 1
      graph[u].append((v, 1))
      graph[v].append((u, 1))
      distToDestination = self._dijkstra(graph, source, destination)
      if distToDestination <= target:
        edges[i][2] += target - distToDestination
        for j in range(i + 1, len(edges)):
          if edges[j][2] == -1:
            edges[j][2] = kMax
        return edges

    return []

  def _dijkstra(self, graph: List[List[int]], src: int, dst: int) -> int:
    dist = [math.inf] * len(graph)
    minHeap = []
    dist[src] = 0
    heapq.heappush(minHeap, (dist[src], src))

    while minHeap:
      d, u = heapq.heappop(minHeap)
      if d > dist[u]:
        continue
      for v, w in graph[u]:
        if d + w < dist[v]:
          dist[v] = d + w
          heapq.heappush(minHeap, (dist[v], v))

    return dist[dst]

class Solution {
  public int[][] modifiedGraphEdges(int n, int[][] edges, int source, int destination, int target) {
    final int MAX = 2_000_000_000;
    List<Pair<Integer, Integer>>[] graph = new List[n];

    for (int i = 0; i < n; i++)
      graph[i] = new ArrayList<>();

    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      final int w = edge[2];
      if (w == -1)
        continue;
      graph[u].add(new Pair<>(v, w));
      graph[v].add(new Pair<>(u, w));
    }

    int distToDestination = dijkstra(graph, source, destination);
    if (distToDestination < target)
      return new int[0][];
    if (distToDestination == target) {
      // Change the weights of negative edges to an impossible value.
      for (int[] edge : edges)
        if (edge[2] == -1)
          edge[2] = MAX;
      return edges;
    }

    for (int i = 0; i < edges.length; ++i) {
      final int u = edges[i][0];
      final int v = edges[i][1];
      final int w = edges[i][2];
      if (w != -1)
        continue;
      edges[i][2] = 1;
      graph[u].add(new Pair<>(v, 1));
      graph[v].add(new Pair<>(u, 1));
      distToDestination = dijkstra(graph, source, destination);
      if (distToDestination <= target) {
        edges[i][2] += target - distToDestination;
        // Change the weights of negative edges to an impossible value.
        for (int j = i + 1; j < edges.length; ++j)
          if (edges[j][2] == -1)
            edges[j][2] = MAX;
        return edges;
      }
    }

    return new int[0][];
  }

  private int dijkstra(List<Pair<Integer, Integer>>[] graph, int src, int dst) {
    int[] dist = new int[graph.length];
    Arrays.fill(dist, Integer.MAX_VALUE);

    dist[src] = 0;
    Queue<Pair<Integer, Integer>> minHeap =
        new PriorityQueue<>(Comparator.comparingInt(Pair::getKey)) {
          { offer(new Pair<>(dist[src], src)); } // (d, u)
        };

    while (!minHeap.isEmpty()) {
      final int d = minHeap.peek().getKey();
      final int u = minHeap.poll().getValue();
      if (d > dist[u])
        continue;
      for (Pair<Integer, Integer> pair : graph[u]) {
        final int v = pair.getKey();
        final int w = pair.getValue();
        if (d + w < dist[v]) {
          dist[v] = d + w;
          minHeap.offer(new Pair<>(dist[v], v));
        }
      }
    }

    return dist[dst];
  }
}

class Solution {
 public:
  vector<vector<int>> modifiedGraphEdges(int n, vector<vector<int>>& edges,
                                         int source, int destination,
                                         int target) {
    constexpr int kMax = 2'000'000'000;
    vector<vector<pair<int, int>>> graph(n);

    for (const vector<int>& edge : edges) {
      const int u = edge[0];
      const int v = edge[1];
      const int w = edge[2];
      if (w == -1)
        continue;
      graph[u].emplace_back(v, w);
      graph[v].emplace_back(u, w);
    }

    int distToDestination = dijkstra(graph, source, destination);
    if (distToDestination < target)
      return {};
    if (distToDestination == target) {
      // Change the weights of negative edges to an impossible value.
      for (vector<int>& edge : edges)
        if (edge[2] == -1)
          edge[2] = kMax;
      return edges;
    }

    for (int i = 0; i < edges.size(); ++i) {
      const int u = edges[i][0];
      const int v = edges[i][1];
      int& w = edges[i][2];
      if (w != -1)
        continue;
      w = 1;
      graph[u].emplace_back(v, 1);
      graph[v].emplace_back(u, 1);
      distToDestination = dijkstra(graph, source, destination);
      if (distToDestination <= target) {
        w += target - distToDestination;
        // Change the weights of negative edges to an impossible value.
        for (int j = i + 1; j < edges.size(); ++j)
          if (edges[j][2] == -1)
            edges[j][2] = kMax;
        return edges;
      }
    }

    return {};
  }

 private:
  int dijkstra(const vector<vector<pair<int, int>>>& graph, int src, int dst) {
    vector<int> dist(graph.size(), INT_MAX);

    dist[src] = 0;
    using P = pair<int, int>;  // (d, u)
    priority_queue<P, vector<P>, greater<>> minHeap;
    minHeap.emplace(dist[src], src);

    while (!minHeap.empty()) {
      const auto [d, u] = minHeap.top();
      minHeap.pop();
      if (d > dist[u])
        continue;
      for (const auto& [v, w] : graph[u])
        if (d + w < dist[v]) {
          dist[v] = d + w;
          minHeap.emplace(dist[v], v);
        }
    }

    return dist[dst];
  }
};


You are given a 0-indexed integer array `nums` representing the score of
students in an exam. The teacher would like to form one non-empty group of
students with maximal strength, where the strength of a group of students of
indices `i0`, `i1`, `i2`, ... , `ik` is defined as `nums[i0] * nums[i1] *
nums[i2] * ... * nums[ik]`.

Return the maximum strength of a group the teacher can create.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def maxStrength(self, nums: List[int]) -> int:
    posProd = 1
    negProd = 1
    maxNeg = -math.inf
    negCount = 0
    hasPos = False
    hasZero = False

    for num in nums:
      if num > 0:
        posProd *= num
        hasPos = True
      elif num < 0:
        negProd *= num
        maxNeg = max(maxNeg, num)
        negCount += 1
      else:
        hasZero = True

    if negCount == 0 and not hasPos:
      return 0
    if negCount % 2 == 0:
      return negProd * posProd
    if negCount >= 3:
      return negProd // maxNeg * posProd
    if hasPos:
      return posProd
    if hasZero:
      return 0
    return maxNeg

class Solution {
  public long maxStrength(int[] nums) {
    long posProd = 1;
    long negProd = 1;
    int maxNeg = Integer.MIN_VALUE;
    int negCount = 0;
    boolean hasPos = false;
    boolean hasZero = false;

    for (final int num : nums)
      if (num > 0) {
        posProd *= num;
        hasPos = true;
      } else if (num < 0) {
        negProd *= num;
        maxNeg = Math.max(maxNeg, num);
        ++negCount;
      } else { // num == 0
        hasZero = true;
      }

    if (negCount == 0 && !hasPos)
      return 0;
    if (negCount % 2 == 0)
      return negProd * posProd;
    if (negCount >= 3)
      return negProd / maxNeg * posProd;
    if (hasPos)
      return posProd;
    if (hasZero)
      return 0;
    return maxNeg;
  }
}

class Solution {
 public:
  long long maxStrength(vector<int>& nums) {
    long posProd = 1;
    long negProd = 1;
    int maxNeg = INT_MIN;
    int negCount = 0;
    bool hasPos = false;
    bool hasZero = false;

    for (const int num : nums)
      if (num > 0) {
        posProd *= num;
        hasPos = true;
      } else if (num < 0) {
        negProd *= num;
        maxNeg = max(maxNeg, num);
        ++negCount;
      } else {  // num == 0
        hasZero = true;
      }

    if (negCount == 0 && !hasPos)
      return 0;
    if (negCount % 2 == 0)
      return negProd * posProd;
    if (negCount >= 3)
      return negProd / maxNeg * posProd;
    if (hasPos)
      return posProd;
    if (hasZero)
      return 0;
    return maxNeg;
  }
};


You are given a 0-indexed integer array `nums`, and you are allowed to
traverse between its indices. You can traverse between index `i` and index
`j`, `i != j`, if and only if `gcd(nums[i], nums[j]) > 1`, where `gcd` is the
greatest common divisor.

Your task is to determine if for every pair of indices `i` and `j` in nums,
where `i < j`, there exists a sequence of traversals that can take us from `i`
to `j`.

Return `true` if it is possible to traverse between all such pairs of indices,
or `false` otherwise.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class UnionFind:
  def __init__(self, n: int):
    self.id = list(range(n))
    self.sz = [1] * n

  def unionBySize(self, u: int, v: int) -> None:
    i = self._find(u)
    j = self._find(v)
    if i == j:
      return
    if self.sz[i] < self.sz[j]:
      self.sz[j] += self.sz[i]
      self.id[i] = j
    else:
      self.sz[i] += self.sz[j]
      self.id[j] = i

  def getSize(self, i: int) -> int:
    return self.sz[i]

  def _find(self, u: int) -> int:
    if self.id[u] != u:
      self.id[u] = self._find(self.id[u])
    return self.id[u]

class Solution:
  def canTraverseAllPairs(self, nums: List[int]) -> bool:
    n = len(nums)
    max_num = max(nums)
    maxPrimeFactor = self._sieveEratosthenes(max_num + 1)
    primeToFirstIndex = collections.defaultdict(int)
    uf = UnionFind(n)

    for i, num in enumerate(nums):
      for prime_factor in self._getPrimeFactors(num, maxPrimeFactor):
        if prime_factor in primeToFirstIndex:
          uf.unionBySize(primeToFirstIndex[prime_factor], i)
        else:
          primeToFirstIndex[prime_factor] = i

    return any(uf.getSize(i) == n for i in range(n))

  def _sieveEratosthenes(self, n: int) -> List[int]:
    minPrimeFactors = [i for i in range(n + 1)]
    for i in range(2, int(n**0.5) + 1):
      if minPrimeFactors[i] == i:
        for j in range(i * i, n, i):
          minPrimeFactors[j] = min(minPrimeFactors[j], i)
    return minPrimeFactors

  def _getPrimeFactors(self, num: int, minPrimeFactors: List[int]) -> List[int]:
    primeFactors = []
    while num > 1:
      divisor = minPrimeFactors[num]
      primeFactors.append(divisor)
      while num % divisor == 0:
        num //= divisor
    return primeFactors

class UnionFind {
  public UnionFind(int n) {
    id = new int[n];
    sz = new int[n];
    for (int i = 0; i < n; ++i)
      id[i] = i;
    for (int i = 0; i < n; ++i)
      sz[i] = 1;
  }

  public void unionBySize(int u, int v) {
    final int i = find(u);
    final int j = find(v);
    if (i == j)
      return;
    if (sz[i] < sz[j]) {
      sz[j] += sz[i];
      id[i] = j;
    } else {
      sz[i] += sz[j];
      id[j] = i;
    }
  }

  public int getSize(int i) {
    return sz[i];
  }

  private int[] id;
  private int[] sz;

  private int find(int u) {
    return id[u] == u ? u : (id[u] = find(id[u]));
  }
}

class Solution {
  public boolean canTraverseAllPairs(int[] nums) {
    final int n = nums.length;
    final int mx = Arrays.stream(nums).max().getAsInt();
    final int[] minPrimeFactors = sieveEratosthenes(mx + 1);
    Map<Integer, Integer> primeToFirstIndex = new HashMap<>();
    UnionFind uf = new UnionFind(n);

    for (int i = 0; i < n; ++i)
      for (final int primeFactor : getPrimeFactors(nums[i], minPrimeFactors))
        // `primeFactor` already appeared in the previous indices.
        if (primeToFirstIndex.containsKey(primeFactor))
          uf.unionBySize(primeToFirstIndex.get(primeFactor), i);
        else
          primeToFirstIndex.put(primeFactor, i);

    for (int i = 0; i < n; ++i)
      if (uf.getSize(i) == n)
        return true;
    return false;
  }

  // Gets the minimum prime factor of i, where 1 < i <= n.
  private int[] sieveEratosthenes(int n) {
    int[] minPrimeFactors = new int[n + 1];
    for (int i = 2; i <= n; ++i)
      minPrimeFactors[i] = i;
    for (int i = 2; i * i < n; ++i)
      if (minPrimeFactors[i] == i) // `i` is prime.
        for (int j = i * i; j < n; j += i)
          minPrimeFactors[j] = Math.min(minPrimeFactors[j], i);
    return minPrimeFactors;
  }

  private List<Integer> getPrimeFactors(int num, int[] minPrimeFactors) {
    List<Integer> primeFactors = new ArrayList<>();
    while (num > 1) {
      final int divisor = minPrimeFactors[num];
      primeFactors.add(divisor);
      while (num % divisor == 0)
        num /= divisor;
    }
    return primeFactors;
  }
}

class UnionFind {
 public:
  UnionFind(int n) : id(n), sz(n, 1) {
    iota(id.begin(), id.end(), 0);
  }

  void unionBySize(int u, int v) {
    const int i = find(u);
    const int j = find(v);
    if (i == j)
      return;
    if (sz[i] < sz[j]) {
      sz[j] += sz[i];
      id[i] = j;
    } else {
      sz[i] += sz[j];
      id[j] = i;
    }
  }

  int getSize(int i) {
    return sz[i];
  }

 private:
  vector<int> id;
  vector<int> sz;

  int find(int u) {
    return id[u] == u ? u : id[u] = find(id[u]);
  }
};

class Solution {
 public:
  bool canTraverseAllPairs(vector<int>& nums) {
    const int n = nums.size();
    const int mx = ranges::max(nums);
    const vector<int> minPrimeFactors = sieveEratosthenes(mx + 1);
    unordered_map<int, int> primeToFirstIndex;
    UnionFind uf(n);

    for (int i = 0; i < n; ++i)
      for (const int primeFactor : getPrimeFactors(nums[i], minPrimeFactors))
        // `primeFactor` already appeared in the previous indices.
        if (const auto it = primeToFirstIndex.find(primeFactor);
            it != primeToFirstIndex.cend())
          uf.unionBySize(it->second, i);
        else
          primeToFirstIndex[primeFactor] = i;

    for (int i = 0; i < n; ++i)
      if (uf.getSize(i) == n)
        return true;

    return false;
  }

 private:
  // Gets the minimum prime factor of i, where 1 < i <= n.
  vector<int> sieveEratosthenes(int n) {
    vector<int> minPrimeFactors(n + 1);
    iota(minPrimeFactors.begin() + 2, minPrimeFactors.end(), 2);
    for (int i = 2; i * i < n; ++i)
      if (minPrimeFactors[i] == i)  // `i` is prime.
        for (int j = i * i; j < n; j += i)
          minPrimeFactors[j] = min(minPrimeFactors[j], i);
    return minPrimeFactors;
  }

  vector<int> getPrimeFactors(int num, const vector<int>& minPrimeFactors) {
    vector<int> primeFactors;
    while (num > 1) {
      const int divisor = minPrimeFactors[num];
      primeFactors.push_back(divisor);
      while (num % divisor == 0)
        num /= divisor;
    }
    return primeFactors;
  }
};


You are given two 0-indexed integer arrays `nums1` and `nums2`, each of length
`n`, and a 1-indexed 2D array `queries` where `queries[i] = [xi, yi]`.

For the `ith` query, find the maximum value of `nums1[j] + nums2[j]` among all
indices `j` `(0 <= j < n)`, where `nums1[j] >= xi` and `nums2[j] >= yi`, or -1
if there is no `j` satisfying the constraints.

Return an array `answer` where `answer[i]` is the answer to the `ith` query.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Pair:
  def __init__(self, x: int, y: int):
    self.x = x
    self.y = y

  def __iter__(self):
    yield self.x
    yield self.y

class IndexedQuery:
  def __init__(self, queryIndex: int, minX: int, minY: int):
    self.queryIndex = queryIndex
    self.minX = minX
    self.minY = minY

  def __iter__(self):
    yield self.queryIndex
    yield self.minX
    yield self.minY

class Solution:
  def maximumSumQueries(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:
    pairs = sorted([Pair(nums1[i], nums2[i])
                   for i in range(len(nums1))], key=lambda p: p.x, reverse=True)
    ans = [0] * len(queries)
    stack = []  # [(y, x + y)]

    pairsIndex = 0
    for queryIndex, minX, minY in sorted([IndexedQuery(i, query[0], query[1]) for i, query in enumerate(queries)], key=lambda iq: -iq.minX):
      while pairsIndex < len(pairs) and pairs[pairsIndex].x >= minX:
        x, y = pairs[pairsIndex]
        while stack and x + y >= stack[-1][1]:
          stack.pop()
        if not stack or y > stack[-1][0]:
          stack.append((y, x + y))
        pairsIndex += 1
      j = self._firstGreaterEqual(stack, minY)
      if j == len(stack):
        ans[queryIndex] = -1
      else:
        ans[queryIndex] = stack[j][1]

    return ans

  def _firstGreaterEqual(self, A: List[Tuple[int, int]], target: int) -> int:
    l = 0
    r = len(A)
    while l < r:
      m = (l + r) // 2
      if A[m][0] >= target:
        r = m
      else:
        l = m + 1
    return l

class Solution {
  public int[] maximumSumQueries(int[] nums1, int[] nums2, int[][] queries) {
    MyPair[] pairs = getPairs(nums1, nums2);
    IndexedQuery[] indexedQueries = getIndexedQueries(queries);
    int[] ans = new int[queries.length];
    List<Pair<Integer, Integer>> stack = new ArrayList<>(); // [(y, x + y)]

    int pairsIndex = 0;
    for (IndexedQuery indexedQuery : indexedQueries) {
      final int queryIndex = indexedQuery.queryIndex;
      final int minX = indexedQuery.minX;
      final int minY = indexedQuery.minY;
      while (pairsIndex < pairs.length && pairs[pairsIndex].x >= minX) {
        MyPair pair = pairs[pairsIndex++];
        // x + y is a better candidate. Given that x is decreasing, the
        // condition "x + y >=  stack.get(stack.size() - 1).getValue()" suggests
        // that y is relatively larger, thereby making it a better candidate.
        final int x = pair.x;
        final int y = pair.y;
        while (!stack.isEmpty() && x + y >= stack.get(stack.size() - 1).getValue())
          stack.remove(stack.size() - 1);
        if (stack.isEmpty() || y > stack.get(stack.size() - 1).getKey())
          stack.add(new Pair<>(y, x + y));
      }
      final int j = firstGreaterEqual(stack, minY);
      ans[queryIndex] = j == stack.size() ? -1 : stack.get(j).getValue();
    }

    return ans;
  }

  private record MyPair(int x, int y){};
  private record IndexedQuery(int queryIndex, int minX, int minY){};

  private int firstGreaterEqual(List<Pair<Integer, Integer>> A, int target) {
    int l = 0;
    int r = A.size();
    while (l < r) {
      final int m = (l + r) / 2;
      if (A.get(m).getKey() >= target)
        r = m;
      else
        l = m + 1;
    }
    return l;
  }

  private MyPair[] getPairs(int[] nums1, int[] nums2) {
    MyPair[] pairs = new MyPair[nums1.length];
    for (int i = 0; i < nums1.length; ++i)
      pairs[i] = new MyPair(nums1[i], nums2[i]);
    Arrays.sort(pairs, Comparator.comparing(MyPair::x, Comparator.reverseOrder()));
    return pairs;
  }

  private IndexedQuery[] getIndexedQueries(int[][] queries) {
    IndexedQuery[] indexedQueries = new IndexedQuery[queries.length];
    for (int i = 0; i < queries.length; ++i)
      indexedQueries[i] = new IndexedQuery(i, queries[i][0], queries[i][1]);
    Arrays.sort(indexedQueries,
                Comparator.comparing(IndexedQuery::minX, Comparator.reverseOrder()));
    return indexedQueries;
  }
}

struct Pair {
  int x;
  int y;
};

struct IndexedQuery {
  int queryIndex;
  int minX;
  int minY;
};

class Solution {
 public:
  vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2,
                                vector<vector<int>>& queries) {
    const vector<Pair> pairs = getPairs(nums1, nums2);
    vector<int> ans(queries.size());
    vector<pair<int, int>> stack;  // [(y, x + y)]

    int pairsIndex = 0;
    for (const auto& [queryIndex, minX, minY] : getIndexedQueries(queries)) {
      while (pairsIndex < pairs.size() && pairs[pairsIndex].x >= minX) {
        const auto [x, y] = pairs[pairsIndex++];
        // x + y is a better candidate. Given that x is decreasing, the
        // condition "x + y >= stack.back().second" suggests that y is
        // relatively larger, thereby making it a better candidate.
        while (!stack.empty() && x + y >= stack.back().second)
          stack.pop_back();
        if (stack.empty() || y > stack.back().first)
          stack.emplace_back(y, x + y);
      }
      const auto it = ranges::lower_bound(stack, pair<int, int>{minY, INT_MIN});
      ans[queryIndex] = it == stack.end() ? -1 : it->second;
    }

    return ans;
  }

 private:
  vector<Pair> getPairs(const vector<int>& nums1, const vector<int>& nums2) {
    vector<Pair> pairs;
    for (int i = 0; i < nums1.size(); ++i)
      pairs.push_back({nums1[i], nums2[i]});
    ranges::sort(pairs, ranges::greater{},
                 [](const Pair& pair) { return pair.x; });
    return pairs;
  }

  vector<IndexedQuery> getIndexedQueries(const vector<vector<int>>& queries) {
    vector<IndexedQuery> indexedQueries;
    for (int i = 0; i < queries.size(); ++i)
      indexedQueries.push_back({i, queries[i][0], queries[i][1]});
    ranges::sort(indexedQueries,
                 [](const IndexedQuery& a, const IndexedQuery& b) {
      return a.minX > b.minX;
    });
    return indexedQueries;
  }
};


You are given an integer `n` denoting the total number of servers and a 2D
0-indexed integer array `logs`, where `logs[i] = [server_id, time]` denotes
that the server with id `server_id` received a request at time `time`.

You are also given an integer `x` and a 0-indexed integer array `queries`.

Return a 0-indexed integer array `arr` of length `queries.length` where
`arr[i]` represents the number of servers that did not receive any requests
during the time interval `[queries[i] - x, queries[i]]`.

Note that the time intervals are inclusive.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class IndexedQuery:
  def __init__(self, queryIndex: int, query: int):
    self.queryIndex = queryIndex
    self.query = query

  def __iter__(self):
    yield self.queryIndex
    yield self.query

class Solution:
  def countServers(self, n: int, logs: List[List[int]], x: int, queries: List[int]) -> List[int]:
    ans = [0] * len(queries)
    count = [0] * (n + 1)

    logs.sort(key=lambda log: log[1])

    i = 0
    j = 0
    servers = 0

    for queryIndex, query in sorted([IndexedQuery(i, query) for i, query in enumerate(queries)], key=lambda iq: iq.query):
      while j < len(logs) and logs[j][1] <= query:
        count[logs[j][0]] += 1
        if count[logs[j][0]] == 1:
          servers += 1
        j += 1
      while i < len(logs) and logs[i][1] < query - x:
        count[logs[i][0]] -= 1
        if count[logs[i][0]] == 0:
          servers -= 1
        i += 1
      ans[queryIndex] = n - servers

    return ans

class Solution {
  public int[] countServers(int n, int[][] logs, int x, int[] queries) {
    int[] ans = new int[queries.length];
    int[] count = new int[n + 1];

    Arrays.sort(logs, Comparator.comparingInt(log -> log[1]));

    int i = 0;
    int j = 0;
    int servers = 0;

    // For each query, we care about logs[i..j].
    for (IndexedQuery indexedQuery : getIndexedQueries(queries)) {
      final int queryIndex = indexedQuery.queryIndex;
      final int query = indexedQuery.query;
      for (; j < logs.length && logs[j][1] <= query; ++j)
        if (++count[logs[j][0]] == 1)
          ++servers;
      for (; i < logs.length && logs[i][1] < query - x; ++i)
        if (--count[logs[i][0]] == 0)
          --servers;
      ans[queryIndex] = n - servers;
    }

    return ans;
  }

  private record IndexedQuery(int queryIndex, int query){};

  private IndexedQuery[] getIndexedQueries(int[] queries) {
    IndexedQuery[] indexedQueries = new IndexedQuery[queries.length];
    for (int i = 0; i < queries.length; ++i)
      indexedQueries[i] = new IndexedQuery(i, queries[i]);
    Arrays.sort(indexedQueries, Comparator.comparingInt(IndexedQuery::query));
    return indexedQueries;
  }
}

struct IndexedQuery {
  int queryIndex;
  int query;
};

class Solution {
 public:
  vector<int> countServers(int n, vector<vector<int>>& logs, int x,
                           vector<int>& queries) {
    vector<int> ans(queries.size());
    vector<int> count(n + 1);

    ranges::sort(logs, ranges::less{},
                 [](const vector<int>& log) { return log[1]; });

    int i = 0;
    int j = 0;
    int servers = 0;

    // For each query, we care about logs[i..j].
    for (const auto& [queryIndex, query] : getIndexedQueries(queries)) {
      for (; j < logs.size() && logs[j][1] <= query; ++j)
        if (++count[logs[j][0]] == 1)
          ++servers;
      for (; i < logs.size() && logs[i][1] < query - x; ++i)
        if (--count[logs[i][0]] == 0)
          --servers;
      ans[queryIndex] = n - servers;
    }

    return ans;
  }

 private:
  vector<IndexedQuery> getIndexedQueries(const vector<int>& queries) {
    vector<IndexedQuery> indexedQueries;
    for (int i = 0; i < queries.size(); ++i)
      indexedQueries.push_back({i, queries[i]});
    ranges::sort(indexedQueries,
                 [](const IndexedQuery& a, const IndexedQuery& b) {
      return a.query < b.query;
    });
    return indexedQueries;
  }
};


There are `n` 1-indexed robots, each having a position on a line, health, and
movement direction.

You are given 0-indexed integer arrays `positions`, `healths`, and a string
`directions` (`directions[i]` is either 'L' for left or 'R' for right). All
integers in `positions` are unique.

All robots start moving on the line simultaneously at the same speed in their
given directions. If two robots ever share the same position while moving,
they will collide.

If two robots collide, the robot with lower health is removed from the line,
and the health of the other robot decreases by one. The surviving robot
continues in the same direction it was going. If both robots have the same
health, they are both removed from the line.

Your task is to determine the health of the robots that survive the
collisions, in the same order that the robots were given, i.e. final heath of
robot 1 (if survived), final health of robot 2 (if survived), and so on. If
there are no survivors, return an empty array.

Return an array containing the health of the remaining robots (in the order
they were given in the input), after no further collisions can occur.

Note: The positions may be unsorted.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator
from dataclasses import dataclass

@dataclass
class Robot:
  index: int
  position: int
  health: int
  direction: str

class Solution:
  def survivedRobotsHealths(self, positions: List[int], healths: List[int], directions: str) -> List[int]:
    robots = sorted([Robot(index, position, health, direction) for index, (position, health, direction) in enumerate(zip(positions, healths, directions))], key=lambda robot: robot.position)
    stack: List[Robot] = []

    for robot in robots:
      if robot.direction == 'R':
        stack.append(robot)
        continue
      while stack and stack[-1].direction == 'R' and robot.health > 0:
        if stack[-1].health == robot.health:
          stack.pop()
          robot.health = 0
        elif stack[-1].health < robot.health:
          stack.pop()
          robot.health -= 1
        else:
          stack[-1].health -= 1
          robot.health = 0
      if robot.health > 0:
        stack.append(robot)

    stack.sort(key=lambda robot: robot.index)
    return [robot.health for robot in stack]

class Robot {
  public int index;
  public int position;
  public int health;
  public char direction;
  public Robot(int index, int position, int health, char direction) {
    this.index = index;
    this.position = position;
    this.health = health;
    this.direction = direction;
  }
}

class Solution {
  public List<Integer> survivedRobotsHealths(int[] positions, int[] healths, String directions) {
    List<Integer> ans = new ArrayList<>();
    Robot[] robots = new Robot[positions.length];
    List<Robot> stack = new ArrayList<>(); // running robots

    for (int i = 0; i < positions.length; ++i)
      robots[i] = new Robot(i, positions[i], healths[i], directions.charAt(i));

    Arrays.sort(robots, Comparator.comparingInt((Robot robot) -> robot.position));

    for (Robot robot : robots) {
      if (robot.direction == 'R') {
        stack.add(robot);
        continue;
      }
      // Collide with robots going right if any.
      while (!stack.isEmpty() && stack.get(stack.size() - 1).direction == 'R' && robot.health > 0) {
        if (stack.get(stack.size() - 1).health == robot.health) {
          stack.remove(stack.size() - 1);
          robot.health = 0;
        } else if (stack.get(stack.size() - 1).health < robot.health) {
          stack.remove(stack.size() - 1);
          robot.health -= 1;
        } else { // stack[-1].health > robot.health
          stack.get(stack.size() - 1).health -= 1;
          robot.health = 0;
        }
      }
      if (robot.health > 0)
        stack.add(robot);
    }

    stack.sort(Comparator.comparingInt((Robot robot) -> robot.index));

    for (Robot robot : stack)
      ans.add(robot.health);

    return ans;
  }
}

struct Robot {
  int index;
  int position;
  int health;
  char direction;
};

class Solution {
 public:
  vector<int> survivedRobotsHealths(vector<int>& positions,
                                    vector<int>& healths, string directions) {
    vector<int> ans;
    vector<Robot> robots;
    vector<Robot> stack;  // the runnnig robots

    for (int i = 0; i < positions.size(); ++i)
      robots.push_back(Robot{i, positions[i], healths[i], directions[i]});

    ranges::sort(robots, ranges::less{},
                 [](const Robot& robot) { return robot.position; });

    for (Robot& robot : robots) {
      if (robot.direction == 'R') {
        stack.push_back(robot);
        continue;
      }
      // Collide with robots going right if any.
      while (!stack.empty() && stack.back().direction == 'R' &&
             robot.health > 0) {
        if (stack.back().health == robot.health) {
          stack.pop_back();
          robot.health = 0;
        } else if (stack.back().health < robot.health) {
          stack.pop_back();
          robot.health -= 1;
        } else {  // stack.back().health > robot.health
          stack.back().health -= 1;
          robot.health = 0;
        }
      }
      if (robot.health > 0)
        stack.push_back(robot);
    }

    ranges::sort(stack, ranges::less{},
                 [](const Robot& robot) { return robot.index; });

    for (const Robot& robot : stack)
      ans.push_back(robot.health);

    return ans;
  }
};


You are given a 0-indexed 2D matrix `grid` of size `n x n`, where `(r, c)`
represents:

* A cell containing a thief if `grid[r][c] = 1`
* An empty cell if `grid[r][c] = 0`

You are initially positioned at cell `(0, 0)`. In one move, you can move to
any adjacent cell in the grid, including cells containing thieves.

The safeness factor of a path on the grid is defined as the minimum manhattan
distance from any cell in the path to any thief in the grid.

Return the maximum safeness factor of all paths leading to cell `(n - 1, n -
1)`.

An adjacent cell of cell `(r, c)`, is one of the cells `(r, c + 1)`, `(r, c -
1)`, `(r + 1, c)` and `(r - 1, c)` if it exists.

The Manhattan distance between two cells `(a, b)` and `(x, y)` is equal to `|a
- x| + |b - y|`, where `|val|` denotes the absolute value of val.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def maximumSafenessFactor(self, grid: List[List[int]]) -> int:
    self.dirs = ((0, 1), (1, 0), (0, -1), (-1, 0))
    n = len(grid)
    distToThief = self._getDistToThief(grid)

    def hasValidPath(safeness: int) -> bool:
      if distToThief[0][0] < safeness:
        return False

      q = collections.deque([(0, 0)])
      seen = {(0, 0)}

      while q:
        i, j = q.popleft()
        if distToThief[i][j] < safeness:
          continue
        if i == n - 1 and j == n - 1:
          return True
        for dx, dy in self.dirs:
          x = i + dx
          y = j + dy
          if x < 0 or x == n or y < 0 or y == n:
            continue
          if (x, y) in seen:
            continue
          q.append((x, y))
          seen.add((x, y))

      return False

    return bisect.bisect_left(range(n * 2), True, key=lambda m: not hasValidPath(m)) - 1

  def _getDistToThief(self, grid: List[List[int]]) -> List[List[int]]:
    n = len(grid)
    distToThief = [[0] * n for _ in range(n)]
    q = collections.deque()
    seen = set()

    for i in range(n):
      for j in range(n):
        if grid[i][j] == 1:
          q.append((i, j))
          seen.add((i, j))

    dist = 0
    while q:
      for _ in range(len(q)):
        i, j = q.popleft()
        distToThief[i][j] = dist
        for dx, dy in self.dirs:
          x = i + dx
          y = j + dy
          if x < 0 or x == n or y < 0 or y == n:
            continue
          if (x, y) in seen:
            continue
          q.append((x, y))
          seen.add((x, y))
      dist += 1

    return distToThief

class Solution {
  public int maximumSafenessFactor(List<List<Integer>> grid) {
    int[][] distToThief = getDistToThief(grid);
    int l = 0;
    int r = grid.size() * 2;

    while (l < r) {
      final int m = (l + r) / 2;
      if (hasValidPath(distToThief, m))
        l = m + 1;
      else
        r = m;
    }

    return l - 1;
  }

  private static final int[][] DIRS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

  private boolean hasValidPath(int[][] distToThief, int safeness) {
    if (distToThief[0][0] < safeness)
      return false;

    final int n = distToThief.length;
    Queue<Pair<Integer, Integer>> q = new ArrayDeque<>(List.of(new Pair<>(0, 0)));
    boolean[][] seen = new boolean[n][n];
    seen[0][0] = true;

    while (!q.isEmpty()) {
      final int i = q.peek().getKey();
      final int j = q.poll().getValue();
      if (distToThief[i][j] < safeness)
        continue;
      if (i == n - 1 && j == n - 1)
        return true;
      for (int[] dir : DIRS) {
        final int x = i + dir[0];
        final int y = j + dir[1];
        if (x < 0 || x == n || y < 0 || y == n)
          continue;
        if (seen[x][y])
          continue;
        q.offer(new Pair<>(x, y));
        seen[x][y] = true;
      }
    }

    return false;
  }

  private int[][] getDistToThief(List<List<Integer>> grid) {
    final int n = grid.size();
    int[][] distToThief = new int[n][n];
    Queue<Pair<Integer, Integer>> q = new ArrayDeque<>();
    boolean[][] seen = new boolean[n][n];

    for (int i = 0; i < n; ++i)
      for (int j = 0; j < n; ++j)
        if (grid.get(i).get(j) == 1) {
          q.offer(new Pair<>(i, j));
          seen[i][j] = true;
        }

    for (int dist = 0; !q.isEmpty(); ++dist) {
      for (int sz = q.size(); sz > 0; --sz) {
        final int i = q.peek().getKey();
        final int j = q.poll().getValue();
        distToThief[i][j] = dist;
        for (int[] dir : DIRS) {
          final int x = i + dir[0];
          final int y = j + dir[1];
          if (x < 0 || x == n || y < 0 || y == n)
            continue;
          if (seen[x][y])
            continue;
          q.offer(new Pair<>(x, y));
          seen[x][y] = true;
        }
      }
    }

    return distToThief;
  }
}

class Solution {
 public:
  int maximumSafenessFactor(vector<vector<int>>& grid) {
    const vector<vector<int>> distToThief = getDistToThief(grid);
    int l = 0;
    int r = grid.size() * 2;

    while (l < r) {
      const int m = (l + r) / 2;
      if (hasValidPath(distToThief, m))
        l = m + 1;
      else
        r = m;
    }

    return l - 1;
  }

 private:
  static constexpr int kDirs[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

  bool hasValidPath(const vector<vector<int>>& distToThief, int safeness) {
    if (distToThief[0][0] < safeness)
      return false;

    const int n = distToThief.size();
    queue<pair<int, int>> q{{{0, 0}}};
    vector<vector<bool>> seen(n, vector<bool>(n));
    seen[0][0] = true;

    while (!q.empty()) {
      const auto [i, j] = q.front();
      q.pop();
      if (distToThief[i][j] < safeness)
        continue;
      if (i == n - 1 && j == n - 1)
        return true;
      for (const auto& [dx, dy] : kDirs) {
        const int x = i + dx;
        const int y = j + dy;
        if (x < 0 || x == n || y < 0 || y == n)
          continue;
        if (seen[x][y])
          continue;
        q.emplace(x, y);
        seen[x][y] = true;
      }
    }

    return false;
  }

  vector<vector<int>> getDistToThief(const vector<vector<int>>& grid) {
    const int n = grid.size();
    vector<vector<int>> distToThief(n, vector<int>(n));
    queue<pair<int, int>> q;
    vector<vector<bool>> seen(n, vector<bool>(n));

    for (int i = 0; i < n; ++i)
      for (int j = 0; j < n; ++j)
        if (grid[i][j] == 1) {
          q.emplace(i, j);
          seen[i][j] = true;
        }

    for (int dist = 0; !q.empty(); ++dist) {
      for (int sz = q.size(); sz > 0; --sz) {
        const auto [i, j] = q.front();
        q.pop();
        distToThief[i][j] = dist;
        for (const auto& [dx, dy] : kDirs) {
          const int x = i + dx;
          const int y = j + dy;
          if (x < 0 || x == n || y < 0 || y == n)
            continue;
          if (seen[x][y])
            continue;
          q.emplace(x, y);
          seen[x][y] = true;
        }
      }
    }

    return distToThief;
  }
};


You are given an array `nums` of `n` positive integers and an integer `k`.

Initially, you start with a score of `1`. You have to maximize your score by
applying the following operation at most `k` times:

* Choose any non-empty subarray `nums[l, ..., r]` that you haven't chosen previously.
* Choose an element `x` of `nums[l, ..., r]` with the highest prime score. If multiple such elements exist, choose the one with the smallest index.
* Multiply your score by `x`.

Here, `nums[l, ..., r]` denotes the subarray of `nums` starting at index `l`
and ending at the index `r`, both ends being inclusive.

The prime score of an integer `x` is equal to the number of distinct prime
factors of `x`. For example, the prime score of `300` is `3` since `300 = 2 *
2 * 3 * 5 * 5`.

Return the maximum possible score after applying at most `k` operations.

Since the answer may be large, return it modulo `109 + 7`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def maximumScore(self, nums: List[int], k: int) -> int:
    kMod = 1_000_000_007
    n = len(nums)
    ans = 1
    minPrimeFactors = self._sieveEratosthenes(max(nums) + 1)
    primeScores = [self._getPrimeScore(num, minPrimeFactors) for num in nums]
    left = [-1] * n
    right = [n] * n
    stack = []

    for i in reversed(range(n)):
      while stack and primeScores[stack[-1]] <= primeScores[i]:
        left[stack.pop()] = i
      stack.append(i)

    stack = []

    for i in range(n):
      while stack and primeScores[stack[-1]] < primeScores[i]:
        right[stack.pop()] = i
      stack.append(i)

    numAndIndexes = [(num, i) for i, num in enumerate(nums)]

    def modPow(x: int, n: int) -> int:
      if n == 0:
        return 1
      if n & 1:
        return x * modPow(x, n - 1) % kMod
      return modPow(x * x % kMod, n // 2)

    for num, i in sorted(numAndIndexes, key=lambda x: (-x[0], x[1])):
      rangeCount = (i - left[i]) * (right[i] - i)
      actualCount = min(rangeCount, k)
      k -= actualCount
      ans *= modPow(num, actualCount)
      ans %= kMod

    return ans

  def _sieveEratosthenes(self, n: int) -> List[int]:
    minPrimeFactors = [i for i in range(n + 1)]
    for i in range(2, int(n**0.5) + 1):
      if minPrimeFactors[i] == i:
        for j in range(i * i, n, i):
          minPrimeFactors[j] = min(minPrimeFactors[j], i)
    return minPrimeFactors

  def _getPrimeScore(self, num: int, minPrimeFactors: List[int]) -> int:
    primeFactors = set()
    while num > 1:
      divisor = minPrimeFactors[num]
      primeFactors.add(divisor)
      while num % divisor == 0:
        num //= divisor
    return len(primeFactors)

class Solution {
  public int maximumScore(List<Integer> nums, int k) {
    final int n = nums.size();
    final int mx = Collections.max(nums);
    final int[] minPrimeFactors = sieveEratosthenes(mx + 1);
    final int[] primeScores = getPrimeScores(nums, minPrimeFactors);
    int ans = 1;
    // left[i] := the next index on the left (if any)
    //            s.t. primeScores[left[i]] >= primeScores[i]
    int[] left = new int[n];
    Arrays.fill(left, -1);
    // right[i] := the next index on the right (if any)
    //             s.t. primeScores[right[i]] > primeScores[i]
    int[] right = new int[n];
    Arrays.fill(right, n);
    Deque<Integer> stack = new ArrayDeque<>();

    // Find the next indices on the left where `primeScores` are greater or equal.
    for (int i = n - 1; i >= 0; --i) {
      while (!stack.isEmpty() && primeScores[stack.peek()] <= primeScores[i])
        left[stack.pop()] = i;
      stack.push(i);
    }

    stack.clear();

    // Find the next indices on the right where `primeScores` are greater.
    for (int i = 0; i < n; ++i) {
      while (!stack.isEmpty() && primeScores[stack.peek()] < primeScores[i])
        right[stack.pop()] = i;
      stack.push(i);
    }

    Pair<Integer, Integer>[] numAndIndexes = new Pair[n];

    for (int i = 0; i < n; ++i)
      numAndIndexes[i] = new Pair<>(nums.get(i), i);

    Arrays.sort(numAndIndexes,
                Comparator.comparing(Pair<Integer, Integer>::getKey, Comparator.reverseOrder())
                    .thenComparingInt(Pair<Integer, Integer>::getValue));

    for (Pair<Integer, Integer> numAndIndex : numAndIndexes) {
      final int num = numAndIndex.getKey();
      final int i = numAndIndex.getValue();
      // nums[i] is the maximum value in the range [left[i] + 1, right[i] - 1]
      // So, there are (i - left[i]) * (right[i] - 1) ranges where nums[i] will
      // be chosen.
      final long rangeCount = (long) (i - left[i]) * (right[i] - i);
      final long actualCount = Math.min(rangeCount, (long) k);
      k -= actualCount;
      ans = (int) ((1L * ans * modPow(num, actualCount)) % MOD);
    }

    return ans;
  }

  private static final int MOD = 1_000_000_007;

  private long modPow(long x, long n) {
    if (n == 0)
      return 1;
    if (n % 2 == 1)
      return x * modPow(x, n - 1) % MOD;
    return modPow(x * x % MOD, n / 2);
  }

  // Gets the minimum prime factor of i, where 1 < i <= n.
  private int[] sieveEratosthenes(int n) {
    int[] minPrimeFactors = new int[n + 1];
    for (int i = 2; i <= n; ++i)
      minPrimeFactors[i] = i;
    for (int i = 2; i * i < n; ++i)
      if (minPrimeFactors[i] == i) // `i` is prime.
        for (int j = i * i; j < n; j += i)
          minPrimeFactors[j] = Math.min(minPrimeFactors[j], i);
    return minPrimeFactors;
  }

  private int[] getPrimeScores(List<Integer> nums, int[] minPrimeFactors) {
    int[] primeScores = new int[nums.size()];
    for (int i = 0; i < nums.size(); ++i)
      primeScores[i] = getPrimeScore(nums.get(i), minPrimeFactors);
    return primeScores;
  }

  private int getPrimeScore(int num, int[] minPrimeFactors) {
    Set<Integer> primeFactors = new HashSet<>();
    while (num > 1) {
      final int divisor = minPrimeFactors[num];
      primeFactors.add(divisor);
      while (num % divisor == 0)
        num /= divisor;
    }
    return primeFactors.size();
  }
}

class Solution {
 public:
  int maximumScore(vector<int>& nums, int k) {
    const int n = nums.size();
    const int mx = ranges::max(nums);
    const vector<int> minPrimeFactors = sieveEratosthenes(mx + 1);
    const vector<int> primeScores = getPrimeScores(nums, minPrimeFactors);
    int ans = 1;
    // left[i] := the next index on the left (if any) s.t.
    // primeScores[left[i]] >= primeScores[i]
    vector<int> left(n, -1);
    // right[i] := the next index on the right (if any) s.t.
    // primeScores[right[i]] > primeScores[i]
    vector<int> right(n, n);
    stack<int> stack;

    // Find the next indices on the left where `primeScores` are greater or
    // equal.
    for (int i = n - 1; i >= 0; --i) {
      while (!stack.empty() && primeScores[stack.top()] <= primeScores[i])
        left[stack.top()] = i, stack.pop();
      stack.push(i);
    }

    stack = std::stack<int>();

    // Find the next indices on the right where `primeScores` are greater.
    for (int i = 0; i < n; ++i) {
      while (!stack.empty() && primeScores[stack.top()] < primeScores[i])
        right[stack.top()] = i, stack.pop();
      stack.push(i);
    }

    vector<pair<int, int>> numAndIndexes;

    for (int i = 0; i < n; ++i)
      numAndIndexes.emplace_back(nums[i], i);

    ranges::sort(numAndIndexes,
                 [&](const pair<int, int>& a, const pair<int, int>& b) {
      return a.first == b.first ? a.second < b.second : a.first > b.first;
    });

    for (const auto& [num, i] : numAndIndexes) {
      // nums[i] is the maximum value in the range [left[i] + 1, right[i] - 1]
      // So, there are (i - left[i]) * (right[i] - 1) ranges where nums[i] will
      // be chosen.
      const long rangeCount = static_cast<long>(i - left[i]) * (right[i] - i);
      const long actualCount = min(rangeCount, static_cast<long>(k));
      k -= actualCount;
      ans = static_cast<long>(ans) * modPow(num, actualCount) % kMod;
    }

    return ans;
  }

 private:
  static constexpr int kMod = 1'000'000'007;

  long modPow(long x, long n) {
    if (n == 0)
      return 1;
    if (n % 2 == 1)
      return x * modPow(x % kMod, (n - 1)) % kMod;
    return modPow(x * x % kMod, (n / 2)) % kMod;
  }

  // Gets the minimum prime factor of i, where 1 < i <= n.
  vector<int> sieveEratosthenes(int n) {
    vector<int> minPrimeFactors(n + 1);
    iota(minPrimeFactors.begin() + 2, minPrimeFactors.end(), 2);
    for (int i = 2; i * i < n; ++i)
      if (minPrimeFactors[i] == i)  // `i` is prime.
        for (int j = i * i; j < n; j += i)
          minPrimeFactors[j] = min(minPrimeFactors[j], i);
    return minPrimeFactors;
  }

  vector<int> getPrimeScores(const vector<int>& nums,
                             const vector<int>& minPrimeFactors) {
    vector<int> primeScores;
    for (const int num : nums)
      primeScores.push_back(getPrimeScore(num, minPrimeFactors));
    return primeScores;
  }

  int getPrimeScore(int num, const vector<int>& minPrimeFactors) {
    unordered_set<int> primeFactors;
    while (num > 1) {
      const int divisor = minPrimeFactors[num];
      primeFactors.insert(divisor);
      while (num % divisor == 0)
        num /= divisor;
    }
    return primeFactors.size();
  }
};