A certain bug's home is on the x-axis at position `x`. Help them get there
from position `0`.

The bug jumps according to the following rules:

* It can jump exactly `a` positions forward (to the right).
* It can jump exactly `b` positions backward (to the left).
* It cannot jump backward twice in a row.
* It cannot jump to any `forbidden` positions.

The bug may jump forward beyond its home, but it cannot jump to positions
numbered with negative integers.

Given an array of integers `forbidden`, where `forbidden[i]` means that the
bug cannot jump to the position `forbidden[i]`, and integers `a`, `b`, and
`x`, return the minimum number of jumps needed for the bug to reach its home.
If there is no possible sequence of jumps that lands the bug on position `x`,
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

from enum import Enum

class Direction(Enum):
  kForward = 0
  kBackward = 1

class Solution:
  def minimumJumps(self, forbidden: List[int], a: int, b: int, x: int) -> int:
    furthest = max(x + a + b, max(pos + a + b for pos in forbidden))
    seenForward = {pos for pos in forbidden}
    seenBackward = {pos for pos in forbidden}

    q = collections.deque([(Direction.kForward, 0)])

    ans = 0
    while q:
      for _ in range(len(q)):
        dir, pos = q.popleft()
        if pos == x:
          return ans
        forward = pos + a
        backward = pos - b
        if forward <= furthest and forward not in seenForward:
          seenForward.add(forward)
          q.append((Direction.kForward, forward))
        if dir == Direction.kForward and backward >= 0 and backward not in seenBackward:
          seenBackward.add(backward)
          q.append((Direction.kBackward, backward))
      ans += 1

    return -1

enum Direction { FORWARD, BACKWARD }

class Solution {
  public int minimumJumps(int[] forbidden, int a, int b, int x) {
    int furthest = x + a + b;
    Set<Integer> seenForward = new HashSet<>();
    Set<Integer> seenBackward = new HashSet<>();

    for (final int pos : forbidden) {
      seenForward.add(pos);
      seenBackward.add(pos);
      furthest = Math.max(furthest, pos + a + b);
    }

    // (direction, position)
    Queue<Pair<Direction, Integer>> q = new ArrayDeque<>(List.of(new Pair<>(Direction.FORWARD, 0)));

    for (int ans = 0; !q.isEmpty(); ++ans)
      for (int sz = q.size(); sz > 0; --sz) {
        Direction dir = q.peek().getKey();
        final int pos = q.poll().getValue();
        if (pos == x)
          return ans;
        final int forward = pos + a;
        final int backward = pos - b;
        if (forward <= furthest && seenForward.add(forward))
          q.offer(new Pair<>(Direction.FORWARD, forward));
        // It cannot jump backward twice in a row.
        if (dir == Direction.FORWARD && backward >= 0 && seenBackward.add(backward))
          q.offer(new Pair<>(Direction.BACKWARD, backward));
      }

    return -1;
  }
}

enum class Direction { kForward, kBackward };

class Solution {
 public:
  int minimumJumps(vector<int>& forbidden, int a, int b, int x) {
    int furthest = x + a + b;
    unordered_set<int> seenForward;
    unordered_set<int> seenBackward;

    for (const int pos : forbidden) {
      seenForward.insert(pos);
      seenBackward.insert(pos);
      furthest = max(furthest, pos + a + b);
    }

    // (direction, position)
    queue<pair<Direction, int>> q{{{Direction::kForward, 0}}};

    for (int ans = 0; !q.empty(); ++ans)
      for (int sz = q.size(); sz > 0; --sz) {
        const auto [dir, pos] = q.front();
        q.pop();
        if (pos == x)
          return ans;
        const int forward = pos + a;
        const int backward = pos - b;
        if (forward <= furthest && seenForward.insert(forward).second)
          q.emplace(Direction::kForward, forward);
        // It cannot jump backward twice in a row.
        if (dir == Direction::kForward && backward >= 0 &&
            seenBackward.insert(backward).second)
          q.emplace(Direction::kBackward, backward);
      }

    return -1;
  }
};


You are given an array of `n` integers, `nums`, where there are at most `50`
unique values in the array. You are also given an array of `m` customer order
quantities, `quantity`, where `quantity[i]` is the amount of integers the
`ith` customer ordered. Determine if it is possible to distribute `nums` such
that:

* The `ith` customer gets exactly `quantity[i]` integers,
* The integers the `ith` customer gets are all equal, and
* Every customer is satisfied.

Return `true` if it is possible to distribute `nums` according to the above
conditions.

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 canDistribute(self, nums: List[int], quantity: List[int]) -> bool:
    freqs = list(collections.Counter(nums).values())
    validDistribution = self._getValidDistribution(freqs, quantity)
    n = len(freqs)
    m = len(quantity)
    maxMask = 1 << m
    dp = [[False] * maxMask for _ in range(n + 1)]
    dp[n][maxMask - 1] = True

    for i in range(n - 1, -1, -1):
      for mask in range(maxMask):
        dp[i][mask] = dp[i + 1][mask]
        availableMask = ~mask & (maxMask - 1)
        submask = availableMask
        while submask > 0:
          if validDistribution[i][submask]:
            dp[i][mask] = dp[i][mask] or dp[i + 1][mask | submask]
          submask = (submask - 1) & availableMask

    return dp[0][0]

  def _getValidDistribution(self, freqs: List[int], quantity: List[int]) -> List[List[bool]]:
    maxMask = 1 << len(quantity)
    validDistribution = [[False] * maxMask for _ in range(len(freqs))]
    for i, freq in enumerate(freqs):
      for mask in range(maxMask):
        if freq >= self._getQuantitySum(quantity, mask):
          validDistribution[i][mask] = True
    return validDistribution

  def _getQuantitySum(self, quantity: List[int], mask: int) -> int:
    res=[]
    for i, q in enumerate(quantity):
      if mask >> i & 1:
        res.append(q)
    return sum(res)

class Solution {
  public boolean canDistribute(int[] nums, int[] quantity) {
    List<Integer> freqs = getFreqs(nums);
    // validDistribution[i][j] := true if it's possible to distribute the i-th
    // freq into a subset of quantity represented by the bitmask j
    boolean[][] validDistribution = getValidDistribution(freqs, quantity);
    final int n = freqs.size();
    final int m = quantity.length;
    final int maxMask = 1 << m;
    // dp[i][j] := true if it's possible to distribute freqs[i..n), where j is
    // the bitmask of the selected quantity
    boolean[][] dp = new boolean[n + 1][maxMask];
    dp[n][maxMask - 1] = true;

    for (int i = n - 1; i >= 0; --i)
      for (int mask = 0; mask < maxMask; ++mask) {
        dp[i][mask] = dp[i + 1][mask];
        final int availableMask = ~mask & (maxMask - 1);
        for (int submask = availableMask; submask > 0; submask = (submask - 1) & availableMask)
          if (validDistribution[i][submask])
            dp[i][mask] = dp[i][mask] || dp[i + 1][mask | submask];
      }

    return dp[0][0];
  }

  private List<Integer> getFreqs(int[] nums) {
    List<Integer> freqs = new ArrayList<>();
    Map<Integer, Integer> count = new HashMap<>();
    for (final int num : nums)
      count.merge(num, 1, Integer::sum);
    return new ArrayList<>(count.values());
  }

  boolean[][] getValidDistribution(List<Integer> freqs, int[] quantity) {
    final int maxMask = 1 << quantity.length;
    boolean[][] validDistribution = new boolean[freqs.size()][maxMask];
    for (int i = 0; i < freqs.size(); ++i)
      for (int mask = 0; mask < maxMask; ++mask)
        if (freqs.get(i) >= getQuantitySum(quantity, mask))
          validDistribution[i][mask] = true;
    return validDistribution;
  }

  // Returns the sum of the selected quantity represented by `mask`.
  int getQuantitySum(int[] quantity, int mask) {
    int sum = 0;
    for (int i = 0; i < quantity.length; ++i)
      if ((mask >> i & 1) == 1)
        sum += quantity[i];
    return sum;
  }
}

class Solution {
 public:
  bool canDistribute(vector<int>& nums, vector<int>& quantity) {
    // validDistribution[i][j] := true if it's possible to distribute the i-th
    // freq into a subset of quantity represented by the bitmask j
    const vector<int> freqs = getFreqs(nums);
    const vector<vector<bool>> validDistribution =
        getValidDistribuition(freqs, quantity);
    const int n = freqs.size();
    const int m = quantity.size();
    const int maxMask = 1 << m;
    // dp[i][j] := true if it's possible to distribute freqs[i..n), where j is
    // the bitmask of the selected quantity
    vector<vector<bool>> dp(n + 1, vector<bool>(maxMask));
    dp[n][maxMask - 1] = true;

    for (int i = n - 1; i >= 0; --i)
      for (int mask = 0; mask < maxMask; ++mask) {
        dp[i][mask] = dp[i + 1][mask];
        const int availableMask = ~mask & (maxMask - 1);
        for (int submask = availableMask; submask > 0;
             submask = (submask - 1) & availableMask)
          if (validDistribution[i][submask])
            dp[i][mask] = dp[i][mask] || dp[i + 1][mask | submask];
      }

    return dp[0][0];
  }

 private:
  vector<int> getFreqs(const vector<int>& nums) {
    vector<int> freqs;
    unordered_map<int, int> count;
    for (const int num : nums)
      ++count[num];
    for (const auto& [_, freq] : count)
      freqs.push_back(freq);
    return freqs;
  }

  vector<vector<bool>> getValidDistribuition(const vector<int>& freqs,
                                             const vector<int>& quantity) {
    const int maxMask = 1 << quantity.size();
    vector<vector<bool>> validDistribution(freqs.size(), vector<bool>(maxMask));
    for (int i = 0; i < freqs.size(); ++i)
      for (int mask = 0; mask < maxMask; ++mask)
        if (freqs[i] >= getQuantitySum(quantity, mask))
          validDistribution[i][mask] = true;
    return validDistribution;
  }

  // Returns the sum of the selected quantity represented by `mask`.
  int getQuantitySum(const vector<int>& quantity, int mask) {
    int sum = 0;
    for (int i = 0; i < quantity.size(); ++i)
      if (mask >> i & 1)
        sum += quantity[i];
    return sum;
  }
};


You are given an integer array `nums` and an integer `k`. You are asked to
distribute this array into `k` subsets of equal size such that there are no
two equal elements in the same subset.

A subset's incompatibility is the difference between the maximum and minimum
elements in that array.

Return the minimum possible sum of incompatibilities of the `k` subsets after
distributing the array optimally, or return `-1` if it is not possible.

A subset is a group integers that appear in the array with no particular
order.

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 __init__(self):
    self.kMaxNum = 16

  def minimumIncompatibility(self, nums: List[int], k: int) -> int:
    kMaxCompatibility = (16 - 1) * (16 // 2)
    n = len(nums)
    subsetSize = n // k
    maxMask = 1 << n
    incompatibilities = self._getIncompatibilities(nums, subsetSize)

    dp = [kMaxCompatibility] * maxMask
    dp[0] = 0

    for mask in range(1, maxMask):
      if mask.bit_count() % subsetSize != 0:
        continue
      submask = mask
      while submask > 0:
        if incompatibilities[submask] != -1:
          dp[mask] = min(dp[mask], dp[mask - submask] + incompatibilities[submask])
        submask = (submask - 1) & mask

    if dp[-1] != kMaxCompatibility:
      return dp[-1]
    else:
      return -1

  def _getIncompatibilities(self, nums: List[int], subsetSize: int) -> List[int]:
    maxMask = 1 << len(nums)
    incompatibilities = [-1] * maxMask
    for mask in range(maxMask):
      if mask.bit_count() == subsetSize and self._isUnique(nums, mask, subsetSize):
        incompatibilities[mask] = self._getIncompatibility(nums, mask)
    return incompatibilities

  def _isUnique(self, nums: List[int], mask: int, subsetSize: int) -> bool:
    used = 0
    for i, num in enumerate(nums):
      if mask >> i & 1:
        used |= 1 << num
    return used.bit_count() == subsetSize

  def _getIncompatibility(self, nums: List[int], mask: int) -> int:
    mini = self.kMaxNum
    maxi = 0
    for i, num in enumerate(nums):
      if mask >> i & 1:
        maxi = max(maxi, num)
        mini = min(mini, num)
    return maxi - mini

class Solution {
  public int minimumIncompatibility(int[] nums, int k) {
    final int MAX_COMPATIBILITY = (16 - 1) * (16 / 2);
    final int n = nums.length;
    final int subsetSize = n / k;
    final int maxMask = 1 << n;
    final int[] incompatibilities = getIncompatibilities(nums, subsetSize);
    // dp[i] := the minimum possible sum of incompatibilities of the subset
    // of numbers represented by the bitmask i
    int[] dp = new int[maxMask];
    Arrays.fill(dp, MAX_COMPATIBILITY);
    dp[0] = 0;

    for (int mask = 1; mask < maxMask; ++mask) {
      // The number of 1s in `mask` isn't a multiple of `subsetSize`.
      if (Integer.bitCount(mask) % subsetSize != 0)
        continue;
      // https://cp-algorithms.com/algebra/all-submasks.html
      for (int submask = mask; submask > 0; submask = (submask - 1) & mask)
        if (incompatibilities[submask] != -1) // valid submask
          dp[mask] = Math.min(dp[mask], dp[mask - submask] + incompatibilities[submask]);
    }

    return dp[maxMask - 1] == MAX_COMPATIBILITY ? -1 : dp[maxMask - 1];
  }

  private static final int MAX_NUM = 16;

  // Returns an incompatibilities array where
  // * incompatibilities[i] := the incompatibility of the subset of numbers
  //   represented by the bitmask i
  // * incompatibilities[i] := -1 if the number of 1s in the bitmask i is not
  //   `subsetSize`
  private int[] getIncompatibilities(int[] nums, int subsetSize) {
    final int maxMask = 1 << nums.length;
    int[] incompatibilities = new int[maxMask];
    Arrays.fill(incompatibilities, -1);
    for (int mask = 0; mask < maxMask; ++mask)
      if (Integer.bitCount(mask) == subsetSize && isUnique(nums, mask, subsetSize))
        incompatibilities[mask] = getIncompatibility(nums, mask);
    return incompatibilities;
  }

  // Returns true if the numbers selected by `mask` are unique.
  //
  // e.g. If we call isUnique(0b1010, 2, [1, 2, 1, 4]), `used` variable
  // will be 0b1, which only has one 1 (less than `subsetSize`). In this case,
  // we should return false.
  private boolean isUnique(int[] nums, int mask, int subsetSize) {
    int used = 0;
    for (int i = 0; i < nums.length; ++i)
      if ((mask >> i & 1) == 1)
        used |= 1 << nums[i];
    return Integer.bitCount(used) == subsetSize;
  }

  // Returns the incompatibility of the selected numbers represented by the
  // `mask`.
  private int getIncompatibility(int[] nums, int mask) {
    int mn = MAX_NUM;
    int mx = 0;
    for (int i = 0; i < nums.length; ++i)
      if ((mask >> i & 1) == 1) {
        mx = Math.max(mx, nums[i]);
        mn = Math.min(mn, nums[i]);
      }
    return mx - mn;
  }
}

class Solution {
 public:
  int minimumIncompatibility(vector<int>& nums, int k) {
    constexpr int kMaxCompatibility = (16 - 1) * (16 / 2);
    const int n = nums.size();
    const int subsetSize = n / k;
    const int maxMask = 1 << n;
    const vector<int> incompatibilities =
        getIncompatibilities(nums, subsetSize);
    // dp[i] := the minimum possible sum of incompatibilities of the subset
    // of numbers represented by the bitmask i
    vector<int> dp(maxMask, kMaxCompatibility);
    dp[0] = 0;

    for (unsigned mask = 1; mask < maxMask; ++mask) {
      // The number of 1s in `mask` isn't a multiple of `subsetSize`.
      if (popcount(mask) % subsetSize != 0)
        continue;
      // https://cp-algorithms.com/algebra/all-submasks.html
      for (int submask = mask; submask > 0; submask = (submask - 1) & mask)
        if (incompatibilities[submask] != -1)  // valid subset
          dp[mask] =
              min(dp[mask], dp[mask - submask] + incompatibilities[submask]);
    }

    return dp.back() == kMaxCompatibility ? -1 : dp.back();
  }

 private:
  static constexpr int kMaxNum = 16;

  // Returns an incompatibilities array where
  // * incompatibilities[i] := the incompatibility of the subset of numbers
  //   represented by the bitmask i
  // * incompatibilities[i] := -1 if the number of 1s in the bitmask i is not
  //   `subsetSize`
  vector<int> getIncompatibilities(const vector<int>& nums, int subsetSize) {
    const int maxMask = 1 << nums.size();
    vector<int> incompatibilities(maxMask, -1);
    for (unsigned mask = 0; mask < maxMask; ++mask)
      if (popcount(mask) == subsetSize && isUnique(nums, mask, subsetSize))
        incompatibilities[mask] = getIncompatibility(nums, mask);
    return incompatibilities;
  }

  // Returns true if the numbers selected by `mask` are unique.
  //
  // e.g. If we call isUnique(0b1010, 2, [1, 2, 1, 4]), `used` variable
  // will be 0b1, which only has one 1 (less than `subsetSize`). In this case,
  // we should return false.
  bool isUnique(const vector<int>& nums, int mask, int subsetSize) {
    unsigned used = 0;
    for (int i = 0; i < nums.size(); ++i)
      if (mask >> i & 1)
        used |= 1 << nums[i];
    return popcount(used) == subsetSize;
  }

  // Returns the incompatibility of the selected numbers represented by the
  // `mask`.
  int getIncompatibility(const vector<int>& nums, int mask) {
    int mn = kMaxNum;
    int mx = 0;
    for (int i = 0; i < nums.size(); ++i)
      if (mask >> i & 1) {
        mx = max(mx, nums[i]);
        mn = min(mn, nums[i]);
      }
    return mx - mn;
  }
};


You have the task of delivering some boxes from storage to their ports using
only one ship. However, this ship has a limit on the number of boxes and the
total weight that it can carry.

You are given an array `boxes`, where `boxes[i] = [portsi, weighti]`, and
three integers `portsCount`, `maxBoxes`, and `maxWeight`.

* `portsi` is the port where you need to deliver the `ith` box and `weightsi` is the weight of the `ith` box.
* `portsCount` is the number of ports.
* `maxBoxes` and `maxWeight` are the respective box and weight limits of the ship.

The boxes need to be delivered in the order they are given. The ship will
follow these steps:

* The ship will take some number of boxes from the `boxes` queue, not violating the `maxBoxes` and `maxWeight` constraints.
* For each loaded box in order, the ship will make a trip to the port the box needs to be delivered to and deliver it. If the ship is already at the correct port, no trip is needed, and the box can immediately be delivered.
* The ship then makes a return trip to storage to take more boxes from the queue.

The ship must end at storage after all the boxes have been delivered.

Return the minimum number of trips the ship needs to make to deliver all boxes
to their respective ports.

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 boxDelivering(self, boxes: List[List[int]], portsCount: int, maxBoxes: int, maxWeight: int) -> int:
    n = len(boxes)
    dp = [0] * (n + 1)
    trips = 2
    weight = 0

    l = 0
    for r in range(n):
      weight += boxes[r][1]

      if r > 0 and boxes[r][0] != boxes[r - 1][0]:
        trips += 1

      while r - l + 1 > maxBoxes or weight > maxWeight or (l < r and dp[l + 1] == dp[l]):
        weight -= boxes[l][1]
        if boxes[l][0] != boxes[l + 1][0]:
          trips -= 1
        l += 1

      dp[r + 1] = dp[l] + trips

    return dp[n]

class Solution {
  public int boxDelivering(int[][] boxes, int portsCount, int maxBoxes, int maxWeight) {
    final int n = boxes.length;
    // dp[i] := the minimum trips to deliver boxes[0..i) and return to the
    // storage
    int[] dp = new int[n + 1];
    int trips = 2;
    int weight = 0;

    for (int l = 0, r = 0; r < n; ++r) {
      weight += boxes[r][1];

      // The current box is different from the previous one, need to make one
      // more trip.
      if (r > 0 && boxes[r][0] != boxes[r - 1][0])
        ++trips;

      while (r - l + 1 > maxBoxes || weight > maxWeight ||
             // Loading boxes[l] in the previous turn is always no bad than
             // loading it in this turn.
             (l < r && dp[l + 1] == dp[l])) {
        weight -= boxes[l][1];
        if (boxes[l][0] != boxes[l + 1][0])
          --trips;
        ++l;
      }

      //   the minimum trips to deliver boxes[0..r]
      // = the minimum trips to deliver boxes[0..l) +
      //               trips to deliver boxes[l..r]
      dp[r + 1] = dp[l] + trips;
    }

    return dp[n];
  }
}

class Solution {
 public:
  int boxDelivering(vector<vector<int>>& boxes, int portsCount, int maxBoxes,
                    int maxWeight) {
    const int n = boxes.size();
    // dp[i] := the minimum trips to deliver boxes[0..i) and return to the
    // storage
    vector<int> dp(n + 1);
    int trips = 2;
    int weight = 0;

    for (int l = 0, r = 0; r < n; ++r) {
      weight += boxes[r][1];

      // The current box is different from the previous one, need to make one
      // more trip.
      if (r > 0 && boxes[r][0] != boxes[r - 1][0])
        ++trips;

      while (r - l + 1 > maxBoxes || weight > maxWeight ||
             // Loading boxes[l] in the previous turn is always no bad than
             // loading it in this turn.
             (l < r && dp[l + 1] == dp[l])) {
        weight -= boxes[l][1];
        if (boxes[l][0] != boxes[l + 1][0])
          --trips;
        ++l;
      }

      //   min trips to deliver boxes[0..r]
      // = min trips to deliver boxes[0..l) + trips to deliver boxes[l..r]
      dp[r + 1] = dp[l] + trips;
    }

    return dp[n];
  }
};


There is a special kind of apple tree that grows apples every day for `n`
days. On the `ith` day, the tree grows `apples[i]` apples that will rot after
`days[i]` days, that is on day `i + days[i]` the apples will be rotten and
cannot be eaten. On some days, the apple tree does not grow any apples, which
are denoted by `apples[i] == 0` and `days[i] == 0`.

You decided to eat at most one apple a day (to keep the doctors away). Note
that you can keep eating after the first `n` days.

Given two integer arrays `days` and `apples` of length `n`, return the maximum
number of apples you can eat.

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 eatenApples(self, apples: List[int], days: List[int]) -> int:
    n = len(apples)
    ans = 0
    minHeap = []

    i = 0
    while i < n or minHeap:
      while minHeap and minHeap[0][0] <= i:
        heapq.heappop(minHeap)
      if i < n and apples[i] > 0:
        heapq.heappush(minHeap, (i + days[i], apples[i]))
      if minHeap:
        rottenDay, numApples = heapq.heappop(minHeap)
        if numApples > 1:
          heapq.heappush(minHeap, (rottenDay, numApples - 1))
        ans += 1
      i += 1

    return ans

class Solution {
  public int eatenApples(int[] apples, int[] days) {
    final int n = apples.length;
    int ans = 0;
    // (the rotten day, the number of apples)
    Queue<Pair<Integer, Integer>> minHeap =
        new PriorityQueue<>(Comparator.comparingInt(Pair::getKey));

    for (int i = 0; i < n || !minHeap.isEmpty(); ++i) {
      // Remove the rotten apples.
      while (!minHeap.isEmpty() && minHeap.peek().getKey() <= i)
        minHeap.poll();
      // Add today's apples.
      if (i < n && apples[i] > 0)
        minHeap.offer(new Pair<>(i + days[i], apples[i]));
      // Eat one apple today.
      if (!minHeap.isEmpty()) {
        final int rottenDay = minHeap.peek().getKey();
        final int numApples = minHeap.poll().getValue();
        if (numApples > 1)
          minHeap.offer(new Pair<>(rottenDay, numApples - 1));
        ++ans;
      }
    }

    return ans;
  }
}

class Solution {
 public:
  int eatenApples(vector<int>& apples, vector<int>& days) {
    const int n = apples.size();
    int ans = 0;
    using P = pair<int, int>;  // (the rotten day, the number of apples)
    priority_queue<P, vector<P>, greater<>> minHeap;

    for (int i = 0; i < n || !minHeap.empty(); ++i) {  // i := day
      // Remove the rotten apples.
      while (!minHeap.empty() && minHeap.top().first <= i)
        minHeap.pop();
      // Add today's apples.
      if (i < n && apples[i] > 0)
        minHeap.emplace(i + days[i], apples[i]);
      // Eat one apple today.
      if (!minHeap.empty()) {
        const auto [rottenDay, numApples] = minHeap.top();
        minHeap.pop();
        if (numApples > 1)
          minHeap.emplace(rottenDay, numApples - 1);
        ++ans;
      }
    }

    return ans;
  }
};


You have a 2-D `grid` of size `m x n` representing a box, and you have `n`
balls. The box is open on the top and bottom sides.

Each cell in the box has a diagonal board spanning two corners of the cell
that can redirect a ball to the right or to the left.

* A board that redirects the ball to the right spans the top-left corner to the bottom-right corner and is represented in the grid as `1`.
* A board that redirects the ball to the left spans the top-right corner to the bottom-left corner and is represented in the grid as `-1`.

We drop one ball at the top of each column of the box. Each ball can get stuck
in the box or fall out of the bottom. A ball gets stuck if it hits a "V"
shaped pattern between two boards or if a board redirects the ball into either
wall of the box.

Return an array `answer` of size `n` where `answer[i]` is the column that the
ball falls out of at the bottom after dropping the ball from the `ith` column
at the top, or `-1` if the ball gets stuck in the box.

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 findBall(self, grid: List[List[int]]) -> List[int]:
    m = len(grid)
    n = len(grid[0])
    dp = [i for i in range(n)]
    ans = [-1] * n

    for i in range(m):
      newDp = [-1] * n
      for j in range(n):
        if j + grid[i][j] < 0 or j + grid[i][j] == n:
          continue
        if grid[i][j] == 1 and grid[i][j + 1] == -1 or grid[i][j] == -1 and grid[i][j - 1] == 1:
          continue
        newDp[j + grid[i][j]] = dp[j]
      dp = newDp

    for i, ball in enumerate(dp):
      if ball != -1:
        ans[ball] = i

    return ans

class Solution {
  public int[] findBall(int[][] grid) {
    final int m = grid.length;
    final int n = grid[0].length;
    // dp[i] := status of the i-th column
    // -1 := empty, 0 := b0, 1 := b1, ...
    int[] dp = new int[n];
    // ans[i] := the i-th ball's final position
    int[] ans = new int[n];
    Arrays.fill(ans, -1);

    for (int i = 0; i < n; ++i)
      dp[i] = i;

    for (int i = 0; i < m; ++i) {
      int[] newDp = new int[n];
      Arrays.fill(newDp, -1);
      for (int j = 0; j < n; ++j) {
        // out-of-bounds
        if (j + grid[i][j] < 0 || j + grid[i][j] == n)
          continue;
        // Stuck
        if (grid[i][j] == 1 && grid[i][j + 1] == -1 || grid[i][j] == -1 && grid[i][j - 1] == 1)
          continue;
        newDp[j + grid[i][j]] = dp[j];
      }
      dp = newDp;
    }

    for (int i = 0; i < n; ++i)
      if (dp[i] != -1)
        ans[dp[i]] = i;

    return ans;
  }
}

class Solution {
 public:
  vector<int> findBall(vector<vector<int>>& grid) {
    const int m = grid.size();
    const int n = grid[0].size();
    // dp[i] := status of the i-th column
    // -1 := empty, 0 := b0, 1 := b1, ...
    vector<int> dp(n);
    // ans[i] := the i-th ball's final position
    vector<int> ans(n, -1);

    iota(dp.begin(), dp.end(), 0);

    for (int i = 0; i < m; ++i) {
      vector<int> newDp(n, -1);
      for (int j = 0; j < n; ++j) {
        // out-of-bounds
        if (j + grid[i][j] < 0 || j + grid[i][j] == n)
          continue;
        // Stuck
        if (grid[i][j] == 1 && grid[i][j + 1] == -1 ||
            grid[i][j] == -1 && grid[i][j - 1] == 1)
          continue;
        newDp[j + grid[i][j]] = dp[j];
      }
      dp = std::move(newDp);
    }

    for (int i = 0; i < n; ++i)
      if (dp[i] != -1)
        ans[dp[i]] = i;

    return ans;
  }
};


You are given an array `nums` consisting of non-negative integers. You are
also given a `queries` array, where `queries[i] = [xi, mi]`.

The answer to the `ith` query is the maximum bitwise `XOR` value of `xi` and
any element of `nums` that does not exceed `mi`. In other words, the answer is
`max(nums[j] XOR xi)` for all `j` such that `nums[j] <= mi`. If all elements
in `nums` are larger than `mi`, then the answer is `-1`.

Return an integer array `answer` where `answer.length == queries.length` and
`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, Optional

class TrieNode:
  def __init__(self):
    self.children: List[Optional[TrieNode]] = [None] * 2

class BitTrie:
  def __init__(self, maxBit: int):
    self.maxBit = maxBit
    self.root = TrieNode()

  def insert(self, num: int) -> None:
    node = self.root
    for i in range(self.maxBit, -1, -1):
      bit = num >> i & 1
      if not node.children[bit]:
        node.children[bit] = TrieNode()
      node = node.children[bit]

  def getMaxXor(self, num: int) -> int:
    maxXor = 0
    node = self.root
    for i in range(self.maxBit, -1, -1):
      bit = num >> i & 1
      toggleBit = bit ^ 1
      if node.children[toggleBit]:
        maxXor = maxXor | 1 << i
        node = node.children[toggleBit]
      elif node.children[bit]:
        node = node.children[bit]
      else:
        return 0
    return maxXor

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

  def __iter__(self):
    yield self.queryIndex
    yield self.x
    yield self.m

class Solution:
  def maximizeXor(self, nums: List[int], queries: List[List[int]]) -> List[int]:
    ans = [-1] * len(queries)
    maxBit = int(math.log2(max(max(nums), max(x for x, _ in queries))))
    bitTrie = BitTrie(maxBit)

    nums.sort()

    i = 0
    for queryIndex, x, m in sorted([IndexedQuery(i, x, m) for i, (x, m) in enumerate(queries)], key=lambda iq: iq.m):
      while i < len(nums) and nums[i] <= m:
        bitTrie.insert(nums[i])
        i += 1
      if i > 0 and nums[i - 1] <= m:
        ans[queryIndex] = bitTrie.getMaxXor(x)

    return ans

class TrieNode {
  public TrieNode[] children = new TrieNode[2];
}

class BitTrie {
  public BitTrie(int maxBit) {
    this.maxBit = maxBit;
  }

  public void insert(int num) {
    TrieNode node = root;
    for (int i = maxBit; i >= 0; --i) {
      final int bit = (int) (num >> i & 1);
      if (node.children[bit] == null)
        node.children[bit] = new TrieNode();
      node = node.children[bit];
    }
  }

  public int getMaxXor(int num) {
    int maxXor = 0;
    TrieNode node = root;
    for (int i = maxBit; i >= 0; --i) {
      final int bit = (int) (num >> i & 1);
      final int toggleBit = bit ^ 1;
      if (node.children[toggleBit] != null) {
        maxXor = maxXor | 1 << i;
        node = node.children[toggleBit];
      } else if (node.children[bit] != null) {
        node = node.children[bit];
      } else { // There's nothing in the Bit Trie.
        return 0;
      }
    }
    return maxXor;
  }

  private int maxBit;
  private TrieNode root = new TrieNode();
}

class Solution {
  public int[] maximizeXor(int[] nums, int[][] queries) {
    int[] ans = new int[queries.length];
    Arrays.fill(ans, -1);
    final int maxNumInNums = Arrays.stream(nums).max().getAsInt();
    final int maxNumInQuery = Arrays.stream(queries).mapToInt(query -> query[0]).max().getAsInt();
    final int maxBit = (int) (Math.log(Math.max(maxNumInNums, maxNumInQuery)) / Math.log(2));
    BitTrie bitTrie = new BitTrie(maxBit);

    Arrays.sort(nums);

    int i = 0; // nums' index
    for (IndexedQuery indexedQuery : getIndexedQueries(queries)) {
      final int queryIndex = indexedQuery.queryIndex;
      final int x = indexedQuery.x;
      final int m = indexedQuery.m;
      while (i < nums.length && nums[i] <= m)
        bitTrie.insert(nums[i++]);
      if (i > 0 && nums[i - 1] <= m)
        ans[queryIndex] = bitTrie.getMaxXor(x);
    }

    return ans;
  }

  private record IndexedQuery(int queryIndex, int x, int m){};

  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.comparingInt(IndexedQuery::m));
    return indexedQueries;
  }
}

struct TrieNode {
  vector<shared_ptr<TrieNode>> children;
  TrieNode() : children(2) {}
};

class BitTrie {
 public:
  BitTrie(int maxBit) : maxBit(maxBit) {}

  void insert(int num) {
    shared_ptr<TrieNode> node = root;
    for (int i = maxBit; i >= 0; --i) {
      const int bit = num >> i & 1;
      if (node->children[bit] == nullptr)
        node->children[bit] = make_shared<TrieNode>();
      node = node->children[bit];
    }
  }

  int getMaxXor(int num) {
    int maxXor = 0;
    shared_ptr<TrieNode> node = root;
    for (int i = maxBit; i >= 0; --i) {
      const int bit = num >> i & 1;
      const int toggleBit = bit ^ 1;
      if (node->children[toggleBit] != nullptr) {
        maxXor = maxXor | 1 << i;
        node = node->children[toggleBit];
      } else if (node->children[bit] != nullptr) {
        node = node->children[bit];
      } else {  // There's nothing in the Bit Trie.
        return 0;
      }
    }
    return maxXor;
  }

 private:
  const int maxBit;
  shared_ptr<TrieNode> root = make_shared<TrieNode>();
};

struct IndexedQuery {
  int queryIndex;
  int x;
  int m;
};

class Solution {
 public:
  vector<int> maximizeXor(vector<int>& nums, vector<vector<int>>& queries) {
    vector<int> ans(queries.size(), -1);
    const int maxNumInNums = ranges::max(nums);
    const int maxNumInQuery = ranges::max_element(queries, ranges::less{},
                                                  [](const vector<int>& query) {
      return query[0];
    })->at(0);
    const int maxBit = static_cast<int>(log2(max(maxNumInNums, maxNumInQuery)));
    BitTrie bitTrie(maxBit);

    ranges::sort(nums);

    int i = 0;  // nums' index
    for (const auto& [queryIndex, x, m] : getIndexedQueries(queries)) {
      while (i < nums.size() && nums[i] <= m)
        bitTrie.insert(nums[i++]);
      if (i > 0 && nums[i - 1] <= m)
        ans[queryIndex] = bitTrie.getMaxXor(x);
    }

    return ans;
  }

 private:
  vector<IndexedQuery> getIndexedQueries(const vector<vector<int>>& queries) {
    vector<IndexedQuery> indexedQueries;
    for (int i = 0; i < queries.size(); ++i)
      indexedQueries.emplace_back(i, queries[i][0], queries[i][1]);
    ranges::sort(
        indexedQueries, ranges::less{},
        [](const IndexedQuery& indexedQuery) { return indexedQuery.m; });
    return indexedQueries;
  }
};


You are given a string `s` and two integers `x` and `y`. You can perform two
types of operations any number of times.

* Remove substring `"ab"` and gain `x` points.
* For example, when removing `"ab"` from `"cabxbae"` it becomes `"cxbae"`.
* Remove substring `"ba"` and gain `y` points.
* For example, when removing `"ba"` from `"cabxbae"` it becomes `"cabxe"`.

Return the maximum points you can gain after applying the above operations on
`s`.

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 maximumGain(self, s: str, x: int, y: int) -> int:
    if x > y:
      return self._gain(s, 'ab', x, 'ba', y)
    else:
      return self._gain(s, 'ba', y, 'ab', x)

  def _gain(self, s: str, sub1: str, point1: int, sub2: str, point2: int) -> int:
    points = 0
    stack1 = []
    stack2 = []

    for c in s:
      if stack1 and stack1[-1] == sub1[0] and c == sub1[1]:
        stack1.pop()
        points += point1
      else:
        stack1.append(c)

    for c in stack1:
      if stack2 and stack2[-1] == sub2[0] and c == sub2[1]:
        stack2.pop()
        points += point2
      else:
        stack2.append(c)

    return points

class Solution {
  public int maximumGain(String s, int x, int y) {
    // The assumption that gain("ab") > gain("ba") while removing "ba" first is
    // optimal is contradicted. Only "b(ab)a" satisfies the condition of
    // preventing two "ba" removals, but after removing "ab", we can still
    // remove one "ba", resulting in a higher gain. Thus, removing "ba" first is
    // not optimal.
    return x > y ? gain(s, "ab", x, "ba", y) : gain(s, "ba", y, "ab", x);
  }

  // Returns the points gained by first removing sub1 ("ab" | "ba") from s with
  // point1, then removing sub2 ("ab" | "ba") from s with point2.
  private int gain(final String s, final String sub1, int point1, final String sub2, int point2) {
    int points = 0;
    Stack<Character> stack1 = new Stack<>();
    Stack<Character> stack2 = new Stack<>();

    // Remove "sub1" from s with point1 gain.
    for (final char c : s.toCharArray())
      if (!stack1.isEmpty() && stack1.peek() == sub1.charAt(0) && c == sub1.charAt(1)) {
        stack1.pop();
        points += point1;
      } else {
        stack1.push(c);
      }

    // Remove "sub2" from s with point2 gain.
    for (final char c : stack1)
      if (!stack2.isEmpty() && stack2.peek() == sub2.charAt(0) && c == sub2.charAt(1)) {
        stack2.pop();
        points += point2;
      } else {
        stack2.push(c);
      }

    return points;
  }
}

class Solution {
 public:
  int maximumGain(string s, int x, int y) {
    // The assumption that gain("ab") > gain("ba") while removing "ba" first is
    // optimal is contradicted. Only "b(ab)a" satisfies the condition of
    // preventing two "ba" removals, but after removing "ab", we can still
    // remove one "ba", resulting in a higher gain. Thus, removing "ba" first is
    // not optimal.
    return x > y ? gain(s, "ab", x, "ba", y) : gain(s, "ba", y, "ab", x);
  }

 private:
  // Returns the points gained by first removing sub1 ("ab" | "ba") from s with
  // point1, then removing sub2 ("ab" | "ba") from s with point2.
  int gain(const string& s, const string& sub1, int point1, const string& sub2,
           int point2) {
    int points = 0;
    vector<char> stack1;
    vector<char> stack2;

    // Remove "sub1" from s with point1 gain.
    for (const char c : s)
      if (!stack1.empty() && stack1.back() == sub1[0] && c == sub1[1]) {
        stack1.pop_back();
        points += point1;
      } else {
        stack1.push_back(c);
      }

    // Remove "sub2" from s with point2 gain.
    for (const char c : stack1)
      if (!stack2.empty() && stack2.back() == sub2[0] && c == sub2[1]) {
        stack2.pop_back();
        points += point2;
      } else {
        stack2.push_back(c);
      }

    return points;
  }
};


You are given an array `pairs`, where `pairs[i] = [xi, yi]`, and:

* There are no duplicates.
* `xi < yi`

Let `ways` be the number of rooted trees that satisfy the following
conditions:

* The tree consists of nodes whose values appeared in `pairs`.
* A pair `[xi, yi]` exists in `pairs` if and only if `xi` is an ancestor of `yi` or `yi` is an ancestor of `xi`.
* Note: the tree does not have to be a binary tree.

Two ways are considered to be different if there is at least one node that has
different parents in both ways.

Return:

* `0` if `ways == 0`
* `1` if `ways == 1`
* `2` if `ways > 1`

A rooted tree is a tree that has a single root node, and all edges are
oriented to be outgoing from the root.

An ancestor of a node is any node on the path from the root to that node
(excluding the node itself). The root has no ancestors.

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 checkWays(self, pairs: List[List[int]]) -> int:
    kMax = 501
    graph = collections.defaultdict(list)
    degrees = [0] * kMax
    connected = [[False] * kMax for _ in range(kMax)]

    for u, v in pairs:
      graph[u].append(v)
      graph[v].append(u)
      degrees[u] += 1
      degrees[v] += 1
      connected[u][v] = True
      connected[v][u] = True

    for _, children in graph.items():
      children.sort(key=lambda a: degrees[a], reverse=True)

    root = next((i for i, d in enumerate(degrees) if d == len(graph) - 1), -1)
    if root == -1:
      return 0

    hasMoreThanOneWay = False

    def dfs(u: int, ancestors: List[int], seen: List[bool]) -> bool:
      nonlocal hasMoreThanOneWay
      seen[u] = True
      for ancestor in ancestors:
        if not connected[u][ancestor]:
          return False
      ancestors.append(u)
      for v in graph[u]:
        if seen[v]:
          continue
        if degrees[v] == degrees[u]:
          hasMoreThanOneWay = True
        if not dfs(v, ancestors, seen):
          return False
      ancestors.pop()
      return True

    if not dfs(root, [], [False] * kMax):
      return 0
    if hasMoreThanOneWay:
      return 2
    else:
      return 1

class Solution {
  public int checkWays(int[][] pairs) {
    final int MAX = 501;
    Map<Integer, List<Integer>> graph = new HashMap<>();
    int[] degrees = new int[MAX];
    boolean[][] connected = new boolean[MAX][MAX];

    for (int[] pair : pairs) {
      final int u = pair[0];
      final int v = pair[1];
      graph.putIfAbsent(u, new ArrayList<>());
      graph.putIfAbsent(v, new ArrayList<>());
      graph.get(u).add(v);
      graph.get(v).add(u);
      ++degrees[u];
      ++degrees[v];
      connected[u][v] = true;
      connected[v][u] = true;
    }

    // For each node, sort its children by degrees in descending order.
    for (final int u : graph.keySet())
      graph.get(u).sort(Comparator.comparingInt(a -> - degrees[a]));

    final int root = getRoot(degrees, graph.keySet().size());
    if (root == -1)
      return 0;
    if (!dfs(graph, root, degrees, connected, new ArrayList<>(), new boolean[MAX]))
      return 0;
    return hasMoreThanOneWay ? 2 : 1;
  }

  private boolean hasMoreThanOneWay = false;

  // Returns the root by finding the node with a degree that equals to n - 1.
  private int getRoot(int[] degrees, int n) {
    for (int i = 1; i < degrees.length; ++i)
      if (degrees[i] == n - 1)
        return i;
    return -1;
  }

  // Returns true if each node rooted at u is connected to all of its ancestors.
  private boolean dfs(Map<Integer, List<Integer>> graph, int u, int[] degrees,
                      boolean[][] connected, List<Integer> ancestors, boolean[] seen) {
    seen[u] = true;

    for (final int ancestor : ancestors)
      if (!connected[u][ancestor])
        return false;

    ancestors.add(u);

    for (final int v : graph.get(u)) {
      if (seen[v])
        continue;
      // We can swap u with v, so there are more than one way.
      if (degrees[v] == degrees[u])
        hasMoreThanOneWay = true;
      if (!dfs(graph, v, degrees, connected, ancestors, seen))
        return false;
    }

    ancestors.remove(ancestors.size() - 1);
    return true;
  }
}

class Solution {
 public:
  int checkWays(vector<vector<int>>& pairs) {
    constexpr int kMax = 501;
    unordered_map<int, vector<int>> graph;
    vector<int> degrees(kMax);
    vector<vector<bool>> connected(kMax, vector<bool>(kMax));

    for (const vector<int>& pair : pairs) {
      const int u = pair[0];
      const int v = pair[1];
      graph[u].push_back(v);
      graph[v].push_back(u);
      ++degrees[u];
      ++degrees[v];
      connected[u][v] = true;
      connected[v][u] = true;
    }

    // For each node, sort its children by degrees in descending order.
    for (auto& [_, children] : graph)
      ranges::sort(children, ranges::greater{},
                   [&degrees](int child) { return degrees[child]; });

    const int root = getRoot(degrees, graph.size());
    if (root == -1)
      return 0;
    if (!dfs(graph, root, degrees, connected, {}, vector<bool>(kMax)))
      return 0;
    return hasMoreThanOneWay ? 2 : 1;
  }

 private:
  bool hasMoreThanOneWay = false;

  // Returns the root by finding the node with a degree that equals to n - 1.
  int getRoot(const vector<int>& degrees, int n) {
    for (int i = 1; i < degrees.size(); ++i)
      if (degrees[i] == n - 1)
        return i;
    return -1;
  }

  // Returns true if each node rooted at u is connected to all of its ancestors.
  bool dfs(const unordered_map<int, vector<int>>& graph, int u,
           vector<int>& degrees, vector<vector<bool>>& connected,
           vector<int>&& ancestors, vector<bool>&& seen) {
    seen[u] = true;

    for (const int ancestor : ancestors)
      if (!connected[u][ancestor])
        return false;

    ancestors.push_back(u);

    for (const int v : graph.at(u)) {
      if (seen[v])
        continue;
      // We can swap u with v, so there are more than one way.
      if (degrees[v] == degrees[u])
        hasMoreThanOneWay = true;
      if (!dfs(graph, v, degrees, connected, std::move(ancestors),
               std::move(seen)))
        return false;
    }

    ancestors.pop_back();
    return true;
  }
};


Minimize Hamming Distance After Swap Operations

You are given two integer arrays, `source` and `target`, both of length `n`.
You are also given an array `allowedSwaps` where each `allowedSwaps[i] = [ai,
bi]` indicates that you are allowed to swap the elements at index `ai` and
index `bi` (0-indexed) of array `source`. Note that you can swap elements at a
specific pair of indices multiple times and in any order.

The Hamming distance of two arrays of the same length, `source` and `target`,
is the number of positions where the elements are different. Formally, it is
the number of indices `i` for `0 <= i <= n-1` where `source[i] != target[i]`
(0-indexed).

Return the minimum Hamming distance of `source` and `target` after performing
any amount of swap operations on array `source`.

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

  def unionByRank(self, u: int, v: int) -> None:
    i = self.find(u)
    j = self.find(v)
    if i == j:
      return
    if self.rank[i] < self.rank[j]:
      self.id[i] = j
    elif self.rank[i] > self.rank[j]:
      self.id[j] = i
    else:
      self.id[i] = j
      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]

class Solution:
  def minimumHammingDistance(self, source: List[int], target: List[int], allowedSwaps: List[List[int]]) -> int:
    n = len(source)
    ans = 0
    uf = UnionFind(n)
    groupIdToCount = [collections.Counter() for _ in range(n)]

    for a, b in allowedSwaps:
      uf.unionByRank(a, b)

    for i in range(n):
      groupIdToCount[uf.find(i)][source[i]] += 1

    for i in range(n):
      groupId = uf.find(i)
      count = groupIdToCount[groupId]
      if target[i] not in count:
        ans += 1
      else:
        count[target[i]] -= 1
        if count[target[i]] == 0:
          del count[target[i]]

    return ans

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

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

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

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

class Solution {
  public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
    final int n = source.length;
    int ans = 0;
    UnionFind uf = new UnionFind(n);
    Map<Integer, Integer>[] groupIdToCount = new Map[n];

    for (int i = 0; i < n; ++i)
      groupIdToCount[i] = new HashMap<>();

    for (int[] allowedSwap : allowedSwaps) {
      final int a = allowedSwap[0];
      final int b = allowedSwap[1];
      uf.unionByRank(a, b);
    }

    for (int i = 0; i < n; ++i)
      groupIdToCount[uf.find(i)].merge(source[i], 1, Integer::sum);

    for (int i = 0; i < n; ++i) {
      final int groupId = uf.find(i);
      Map<Integer, Integer> count = groupIdToCount[groupId];
      if (!count.containsKey(target[i]))
        ++ans;
      else if (count.merge(target[i], -1, Integer::sum) == 0)
        count.remove(target[i]);
    }

    return ans;
  }
}

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

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

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

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

class Solution {
 public:
  int minimumHammingDistance(vector<int>& source, vector<int>& target,
                             vector<vector<int>>& allowedSwaps) {
    const int n = source.size();
    int ans = 0;
    UnionFind uf(n);
    vector<unordered_map<int, int>> groupIdToCount(n);

    for (const vector<int>& allowedSwap : allowedSwaps) {
      const int a = allowedSwap[0];
      const int b = allowedSwap[1];
      uf.unionByRank(a, b);
    }

    for (int i = 0; i < n; ++i)
      ++groupIdToCount[uf.find(i)][source[i]];

    for (int i = 0; i < n; ++i) {
      const int groupId = uf.find(i);
      unordered_map<int, int>& count = groupIdToCount[groupId];
      if (!count.contains(target[i]))
        ++ans;
      else if (--count[target[i]] == 0)
        count.erase(target[i]);
    }

    return ans;
  }
};


You are given a 2D integer array, `queries`. For each `queries[i]`, where
`queries[i] = [ni, ki]`, find the number of different ways you can place
positive integers into an array of size `ni` such that the product of the
integers is `ki`. As the number of ways may be too large, the answer to the
`ith` query is the number of ways modulo `109 + 7`.

Return an integer array `answer` where `answer.length == queries.length`, and
`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 Solution:
  def waysToFillArray(self, queries: List[List[int]]) -> List[int]:
    kMod = 1_000_000_007
    kMax = 10_000
    minPrimeFactors = self._sieveEratosthenes(kMax + 1)

    @functools.lru_cache(None)
    def fact(i: int) -> int:
      return 1 if i <= 1 else i * fact(i - 1) % kMod

    @functools.lru_cache(None)
    def inv(i: int) -> int:
      return pow(i, kMod - 2, kMod)

    @functools.lru_cache(None)
    def nCk(n: int, k: int) -> int:
      return fact(n) * inv(fact(k)) * inv(fact(n - k)) % kMod

    ans = []

    for n, k in queries:
      res = 1
      for freq in self._getPrimeFactorsCount(k, minPrimeFactors).values():
        res = res * nCk(n - 1 + freq, freq) % kMod
      ans.append(res)

    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 _getPrimeFactorsCount(self, num: int, minPrimeFactors: List[int]) -> Dict[int, int]:
    count = collections.Counter()
    while num > 1:
      divisor = minPrimeFactors[num]
      while num % divisor == 0:
        num //= divisor
        count[divisor] += 1
    return count

class Solution {
  public int[] waysToFillArray(int[][] queries) {
    final int MAX = 10_000;
    final int MAX_FREQ = 13; // 2^13 = 8192 < MAX
    final int[] minPrimeFactors = sieveEratosthenes(MAX + 1);
    final long[][] factAndInvFact = getFactAndInvFact(MAX + MAX_FREQ - 1);
    final long[] fact = factAndInvFact[0];
    final long[] invFact = factAndInvFact[1];
    int[] ans = new int[queries.length];

    for (int i = 0; i < queries.length; ++i) {
      final int n = queries[i][0];
      final int k = queries[i][1];
      int res = 1;
      for (final int freq : getPrimeFactorsCount(k, minPrimeFactors).values())
        res = (int) ((long) res * nCk(n - 1 + freq, freq, fact, invFact) % MOD);
      ans[i] = res;
    }

    return ans;
  }

  private static final int MOD = 1_000_000_007;

  // 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 Map<Integer, Integer> getPrimeFactorsCount(int num, int[] minPrimeFactors) {
    Map<Integer, Integer> count = new HashMap<>();
    while (num > 1) {
      final int divisor = minPrimeFactors[num];
      while (num % divisor == 0) {
        num /= divisor;
        count.put(divisor, count.merge(divisor, 1, Integer::sum));
      }
    }
    return count;
  }

  private long[][] getFactAndInvFact(int n) {
    long[] fact = new long[n + 1];
    long[] invFact = new long[n + 1];
    long[] inv = new long[n + 1];
    fact[0] = invFact[0] = 1;
    inv[0] = inv[1] = 1;
    for (int i = 1; i <= n; ++i) {
      if (i >= 2)
        inv[i] = MOD - MOD / i * inv[MOD % i] % MOD;
      fact[i] = fact[i - 1] * i % MOD;
      invFact[i] = invFact[i - 1] * inv[i] % MOD;
    }
    return new long[][] {fact, invFact};
  }

  private int nCk(int n, int k, long[] fact, long[] invFact) {
    return (int) (fact[n] * invFact[k] % MOD * invFact[n - k] % MOD);
  }
}

class Solution {
 public:
  vector<int> waysToFillArray(vector<vector<int>>& queries) {
    constexpr int kMax = 10000;
    constexpr int kMaxFreq = 13;  // 2^13 = 8192 < kMax
    const vector<int> minPrimeFactors = sieveEratosthenes(kMax + 1);
    const auto [fact, invFact] = getFactAndInvFact(kMax + kMaxFreq - 1);
    vector<int> ans;

    for (const vector<int>& query : queries) {
      const int n = query[0];
      const int k = query[1];
      int res = 1;
      for (const auto& [_, freq] : getPrimeFactorsCount(k, minPrimeFactors))
        res = static_cast<long>(res) * nCk(n - 1 + freq, freq, fact, invFact) %
              kMod;
      ans.push_back(res);
    }

    return ans;
  }

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

  // 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;
  }

  unordered_map<int, int> getPrimeFactorsCount(
      int num, const vector<int>& minPrimeFactors) {
    unordered_map<int, int> count;
    while (num > 1) {
      const int divisor = minPrimeFactors[num];
      while (num % divisor == 0) {
        num /= divisor;
        ++count[divisor];
      }
    }
    return count;
  }

  pair<vector<long>, vector<long>> getFactAndInvFact(int n) {
    vector<long> fact(n + 1);
    vector<long> invFact(n + 1);
    vector<long> inv(n + 1);
    fact[0] = invFact[0] = 1;
    inv[0] = inv[1] = 1;
    for (int i = 1; i <= n; ++i) {
      if (i >= 2)
        inv[i] = kMod - kMod / i * inv[kMod % i] % kMod;
      fact[i] = fact[i - 1] * i % kMod;
      invFact[i] = invFact[i - 1] * inv[i] % kMod;
    }
    return {fact, invFact};
  }

  int nCk(int n, int k, const vector<long>& fact, const vector<long>& invFact) {
    return fact[n] * invFact[k] % kMod * invFact[n - k] % kMod;
  }
};


You are given an integer matrix `isWater` of size `m x n` that represents a
map of land and water cells.

* If `isWater[i][j] == 0`, cell `(i, j)` is a land cell.
* If `isWater[i][j] == 1`, cell `(i, j)` is a water cell.

You must assign each cell a height in a way that follows these rules:

* The height of each cell must be non-negative.
* If the cell is a water cell, its height must be `0`.
* Any two adjacent cells must have an absolute height difference of at most `1`. A cell is adjacent to another cell if the former is directly north, east, south, or west of the latter (i.e., their sides are touching).

Find an assignment of heights such that the maximum height in the matrix is
maximized.

Return an integer matrix `height` of size `m x n` where `height[i][j]` is cell
`(i, j)`'s height. If there are multiple solutions, return any of them.

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 highestPeak(self, isWater: List[List[int]]) -> List[List[int]]:
    dirs = ((0, 1), (1, 0), (0, -1), (-1, 0))
    m = len(isWater)
    n = len(isWater[0])
    ans = [[-1] * n for _ in range(m)]
    q = collections.deque()

    for i in range(m):
      for j in range(n):
        if isWater[i][j] == 1:
          q.append((i, j))
          ans[i][j] = 0

    while q:
      i, j = q.popleft()
      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 ans[x][y] != -1:
          continue
        ans[x][y] = ans[i][j] + 1
        q.append((x, y))

    return ans

class Solution {
  public int[][] highestPeak(int[][] isWater) {
    final int[][] DIRS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    final int m = isWater.length;
    final int n = isWater[0].length;
    int[][] ans = new int[m][n];
    Arrays.stream(ans).forEach(A -> Arrays.fill(A, -1));
    Queue<Pair<Integer, Integer>> q = new ArrayDeque<>();

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (isWater[i][j] == 1) {
          q.offer(new Pair<>(i, j));
          ans[i][j] = 0;
        }

    while (!q.isEmpty()) {
      final int i = q.peek().getKey();
      final int j = q.poll().getValue();
      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 (ans[x][y] != -1)
          continue;
        ans[x][y] = ans[i][j] + 1;
        q.offer(new Pair<>(x, y));
      }
    }

    return ans;
  }
}

class Solution {
 public:
  vector<vector<int>> highestPeak(vector<vector<int>>& isWater) {
    constexpr int kDirs[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    const int m = isWater.size();
    const int n = isWater[0].size();
    vector<vector<int>> ans(m, vector<int>(n, -1));
    queue<pair<int, int>> q;

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (isWater[i][j] == 1) {
          q.emplace(i, j);
          ans[i][j] = 0;
        }

    while (!q.empty()) {
      const auto [i, j] = q.front();
      q.pop();
      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 (ans[x][y] != -1)
          continue;
        ans[x][y] = ans[i][j] + 1;
        q.emplace(x, y);
      }
    }

    return ans;
  }
};


You are given an undirected graph defined by an integer `n`, the number of
nodes, and a 2D integer array `edges`, the edges in the graph, where `edges[i]
= [ui, vi]` indicates that there is an undirected edge between `ui` and `vi`.
You are also given an integer array `queries`.

Let `incident(a, b)` be defined as the number of edges that are connected to
either node `a` or `b`.

The answer to the `jth` query is the number of pairs of nodes `(a, b)` that
satisfy both of the following conditions:

* `a < b`
* `incident(a, b) > queries[j]`

Return an array `answers` such that `answers.length == queries.length` and
`answers[j]` is the answer of the `jth` query.

Note that there can be multiple edges between the same two nodes.

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 countPairs(self, n: int, edges: List[List[int]], queries: List[int]) -> List[int]:
    ans = [0] * len(queries)

    count = [0] * (n + 1)

    shared = [collections.Counter() for _ in range(n + 1)]

    for u, v in edges:
      count[u] += 1
      count[v] += 1
      shared[min(u, v)][max(u, v)] += 1

    sortedCount = sorted(count)

    for k, query in enumerate(queries):
      i = 1
      j = n
      while i < j:
        if sortedCount[i] + sortedCount[j] > query:
          ans[k] += j - i
          j -= 1
        else:
          i += 1
      for i in range(1, n + 1):
        for j, sh in shared[i].items():
          if count[i] + count[j] > query and count[i] + count[j] - sh <= query:
            ans[k] -= 1

    return ans

class Solution {
  public int[] countPairs(int n, int[][] edges, int[] queries) {
    int[] ans = new int[queries.length];

    // count[i] := the number of edges of node i
    int[] count = new int[n + 1];

    // shared[i][j] := the number of edges incident to i or j, where i < j
    Map<Integer, Integer>[] shared = new Map[n + 1];

    for (int i = 1; i <= n; ++i)
      shared[i] = new HashMap<>();

    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      ++count[u];
      ++count[v];
      shared[Math.min(u, v)].merge(Math.max(u, v), 1, Integer::sum);
    }

    int[] sortedCount = count.clone();
    Arrays.sort(sortedCount);

    int k = 0;
    for (final int query : queries) {
      for (int i = 1, j = n; i < j;)
        if (sortedCount[i] + sortedCount[j] > query)
          // sortedCount[i] + sortedCount[j] > query
          // sortedCount[i + 1] + sortedCount[j] > query
          // ...
          // sortedCount[j - 1] + sortedCount[j] > query
          // So, there are (j - 1) - i + 1 = j - i pairs > query
          ans[k] += (j--) - i;
        else
          ++i;
      for (int i = 1; i <= n; ++i)
        for (Map.Entry<Integer, Integer> p : shared[i].entrySet()) {
          final int j = p.getKey();
          final int sh = p.getValue();
          if (count[i] + count[j] > query && count[i] + count[j] - sh <= query)
            --ans[k];
        }
      ++k;
    }

    return ans;
  }
}

class Solution {
 public:
  vector<int> countPairs(int n, vector<vector<int>>& edges,
                         vector<int>& queries) {
    vector<int> ans(queries.size());

    // count[i] := the number of edges of node i
    vector<int> count(n + 1);

    // shared[i][j] := the number of edges incident to i or j, where i < j
    vector<unordered_map<int, int>> shared(n + 1);

    for (const vector<int>& edge : edges) {
      const int u = edge[0];
      const int v = edge[1];
      ++count[u];
      ++count[v];
      ++shared[min(u, v)][max(u, v)];
    }

    vector<int> sortedCount(count);
    ranges::sort(sortedCount);

    int k = 0;
    for (const int query : queries) {
      for (int i = 1, j = n; i < j;)
        if (sortedCount[i] + sortedCount[j] > query)
          // sortedCount[i] + sortedCount[j] > query
          // sortedCount[i + 1] + sortedCount[j] > query
          // ...
          // sortedCount[j - 1] + sortedCount[j] > query
          // So, there are (j - 1) - i + 1 = j - i pairs > query
          ans[k] += (j--) - i;
        else
          ++i;
      for (int i = 1; i <= n; ++i)
        for (const auto& [j, sh] : shared[i])
          if (count[i] + count[j] > query && count[i] + count[j] - sh <= query)
            --ans[k];
      ++k;
    }

    return ans;
  }
};


Number of Restricted Paths From First to Last Node

There is an undirected weighted connected graph. You are given a positive
integer `n` which denotes that the graph has `n` nodes labeled from `1` to
`n`, and an array `edges` where each `edges[i] = [ui, vi, weighti]` denotes
that there is an edge between nodes `ui` and `vi` with weight equal to
`weighti`.

A path from node `start` to node `end` is a sequence of nodes `[z0, z1, z2,
..., zk]` such that `z0 = start` and `zk = end` and there is an edge between
`zi` and `zi+1` where `0 <= i <= k-1`.

The distance of a path is the sum of the weights on the edges of the path. Let
`distanceToLastNode(x)` denote the shortest distance of a path between node
`n` and node `x`. A restricted path is a path that also satisfies that
`distanceToLastNode(zi) > distanceToLastNode(zi+1)` where `0 <= i <= k-1`.

Return the number of restricted paths from node `1` to node `n`. Since that
number may be too 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 countRestrictedPaths(self, n: int, edges: List[List[int]]) -> int:
    graph = [[] for _ in range(n)]

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

    return self._dijkstra(graph, 0, n - 1)

  def _dijkstra(self, graph: List[List[Tuple[int, int]]], src: int, dst: int) -> int:
    kMod = 10**9 + 7
    ways = [0] * len(graph)
    dist = [math.inf] * len(graph)

    ways[dst] = 1
    dist[dst] = 0
    minHeap = [(dist[dst], dst)]

    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))
        if dist[v] < dist[u]:
          ways[u] += ways[v]
          ways[u] %= kMod

    return ways[src]

class Solution {
  public int countRestrictedPaths(int n, int[][] edges) {
    List<Pair<Integer, Integer>>[] graph = new List[n];
    Arrays.setAll(graph, i -> new ArrayList<>());

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

    return dijkstra(graph, 0, n - 1);
  }

  private int dijkstra(List<Pair<Integer, Integer>>[] graph, int src, int dst) {
    final int MOD = 1_000_000_007;
    // ways[i] := the number of restricted path from i to n
    long[] ways = new long[graph.length];
    // dist[i] := the distance to the last node of i
    long[] dist = new long[graph.length];
    Arrays.fill(dist, Long.MAX_VALUE);

    ways[dst] = 1;
    dist[dst] = 0;
    // (d, u)
    Queue<Pair<Long, Integer>> minHeap =
        new PriorityQueue<>(Comparator.comparingLong(Pair::getKey));
    minHeap.offer(new Pair<>(dist[dst], dst));

    while (!minHeap.isEmpty()) {
      final long 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));
        }
        if (dist[v] < dist[u]) {
          ways[u] += ways[v];
          ways[u] %= MOD;
        }
      }
    }

    return (int) ways[src];
  }
}

class Solution {
 public:
  int countRestrictedPaths(int n, vector<vector<int>>& edges) {
    vector<vector<pair<int, int>>> graph(n);

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

    return dijkstra(graph, 0, n - 1);
  }

 private:
  int dijkstra(const vector<vector<pair<int, int>>>& graph, int src, int dst) {
    constexpr int kMod = 1'000'000'007;
    // ways[i] := the number of restricted path from i to n
    vector<long> ways(graph.size());
    // dist[i] := the distance to the last node of i
    vector<long> dist(graph.size(), LONG_MAX);

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

    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);
        }
        if (dist[v] < dist[u]) {
          ways[u] += ways[v];
          ways[u] %= kMod;
        }
      }
    }

    return ways[src];
  }
};


You are given an array of integers `nums` (0-indexed) and an integer `k`.

The score of a subarray `(i, j)` is defined as `min(nums[i], nums[i+1], ...,
nums[j]) * (j - i + 1)`. A good subarray is a subarray where `i <= k <= j`.

Return the maximum possible score of a good subarray.

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:
    ans = 0
    stack = []

    for i in range(len(nums) + 1):
      while stack and (i == len(nums) or nums[stack[-1]] > nums[i]):
        h = nums[stack.pop()]
        w = i - stack[-1] - 1 if stack else i
        if (not stack or stack[-1] + 1 <= k) and i - 1 >= k:
          ans = max(ans, h * w)
      stack.append(i)

    return ans

class Solution {
  // Similar to 84. Largest Rectangle in Histogram
  public int maximumScore(int[] nums, int k) {
    int ans = 0;
    Deque<Integer> stack = new ArrayDeque<>();

    for (int i = 0; i <= nums.length; ++i) {
      while (!stack.isEmpty() && (i == nums.length || nums[stack.peek()] > nums[i])) {
        final int h = nums[stack.pop()];
        final int w = stack.isEmpty() ? i : i - stack.peek() - 1;
        if ((stack.isEmpty() || stack.peek() + 1 <= k) && i - 1 >= k)
          ans = Math.max(ans, h * w);
      }
      stack.push(i);
    }

    return ans;
  }
}

class Solution {
 public:
  // Similar to 84. Largest Rectangle in Histogram
  int maximumScore(vector<int>& nums, int k) {
    int ans = 0;
    stack<int> stack;

    for (int i = 0; i <= nums.size(); ++i) {
      while (!stack.empty() &&
             (i == nums.size() || nums[stack.top()] > nums[i])) {
        const int h = nums[stack.top()];
        stack.pop();
        const int w = stack.empty() ? i : i - stack.top() - 1;
        if ((stack.empty() || stack.top() + 1 <= k) && i - 1 >= k)
          ans = max(ans, h * w);
      }
      stack.push(i);
    }

    return ans;
  }
};


Number of Different Integers in a String

You are given a string `word` that consists of digits and lowercase English
letters.

You will replace every non-digit character with a space. For example,
`"a123bc34d8ef34"` will become `" 123 34 8 34"`. Notice that you are left with
some integers that are separated by at least one space: `"123"`, `"34"`,
`"8"`, and `"34"`.

Return the number of different integers after performing the replacement
operations on `word`.

Two integers are considered different if their decimal representations without
any leading zeros are different.

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 numDifferentIntegers(self, word: str) -> int:
    nums = set()
    curr = []

    for c in word:
      if c.isdigit():
        curr.append(c)
      elif curr:
        nums.add(''.join(self._removeLeadingZeros(curr)))
        curr = []

    if curr:
      nums.add(''.join(self._removeLeadingZeros(curr)))

    return len(nums)

  def _removeLeadingZeros(self, s: str) -> str:
    index = next((i for i, c in enumerate(s) if c != '0'), -1)
    if index == -1:
      return ['0']
    else:
      return s[index:]

class Solution {
  public int numDifferentIntegers(String word) {
    HashSet<String> nums = new HashSet<>();
    StringBuilder sb = new StringBuilder();

    for (final char c : word.toCharArray())
      if (Character.isDigit(c)) {
        sb.append(c);
      } else if (sb.length() > 0) {
        nums.add(removeLeadingZeros(sb.toString()));
        sb = new StringBuilder();
      }

    if (sb.length() > 0)
      nums.add(removeLeadingZeros(sb.toString()));
    return nums.size();
  }

  private String removeLeadingZeros(final String s) {
    int index = 0;
    while (index < s.length() && s.charAt(index) == '0')
      ++index;
    return index == s.length() ? "0" : s.substring(index);
  }
}

class Solution {
 public:
  int numDifferentIntegers(string word) {
    unordered_set<string> nums;
    string curr;

    for (const char c : word)
      if (isdigit(c)) {
        curr += c;
      } else if (curr.length() > 0) {
        nums.insert(removeLeadingZeros(curr));
        curr = "";
      }

    if (curr.length() > 0)
      nums.insert(removeLeadingZeros(curr));
    return nums.size();
  }

 private:
  string removeLeadingZeros(const string& s) {
    const int index = s.find_first_not_of('0');
    return index == string::npos ? "0" : s.substr(index);
  }
};


There is a directed graph of `n` colored nodes and `m` edges. The nodes are
numbered from `0` to `n - 1`.

You are given a string `colors` where `colors[i]` is a lowercase English
letter representing the color of the `ith` node in this graph (0-indexed). You
are also given a 2D array `edges` where `edges[j] = [aj, bj]` indicates that
there is a directed edge from node `aj` to node `bj`.

A valid path in the graph is a sequence of nodes `x1 -> x2 -> x3 -> ... -> xk`
such that there is a directed edge from `xi` to `xi+1` for every `1 <= i < k`.
The color value of the path is the number of nodes that are colored the most
frequently occurring color along that path.

Return the largest color value of any valid path in the given graph, or `-1`
if the graph contains a cycle.

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 largestPathValue(self, colors: str, edges: List[List[int]]) -> int:
    n = len(colors)
    ans = 0
    processed = 0
    graph = [[] for _ in range(n)]
    inDegrees = [0] * n
    q = collections.deque()
    count = [[0] * 26 for _ in range(n)]

    for u, v in edges:
      graph[u].append(v)
      inDegrees[v] += 1

    for i, degree in enumerate(inDegrees):
      if degree == 0:
        q.append(i)

    while q:
      u = q.popleft()
      processed += 1
      count[u][ord(colors[u]) - ord('a')] += 1
      ans = max(ans, count[u][ord(colors[u]) - ord('a')])
      for v in graph[u]:
        for i in range(26):
          count[v][i] = max(count[v][i], count[u][i])
        inDegrees[v] -= 1
        if inDegrees[v] == 0:
          q.append(v)

    if processed == n:
      return ans
    else:
      return -1

class Solution {
  public int largestPathValue(String colors, int[][] edges) {
    final int n = colors.length();
    int ans = 0;
    int processed = 0;
    List<Integer>[] graph = new List[n];
    int[] inDegrees = new int[n];
    int[][] count = new int[n][26];
    Arrays.setAll(graph, i -> new ArrayList<>());

    // Build the graph.
    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      graph[u].add(v);
      ++inDegrees[v];
    }

    // Perform topological sorting.
    Queue<Integer> q = IntStream.range(0, n)
                           .filter(i -> inDegrees[i] == 0)
                           .boxed()
                           .collect(Collectors.toCollection(ArrayDeque::new));

    while (!q.isEmpty()) {
      final int out = q.poll();
      ++processed;
      ans = Math.max(ans, ++count[out][colors.charAt(out) - 'a']);
      for (final int in : graph[out]) {
        for (int i = 0; i < 26; ++i)
          count[in][i] = Math.max(count[in][i], count[out][i]);
        if (--inDegrees[in] == 0)
          q.offer(in);
      }
    }

    return processed == n ? ans : -1;
  }
}

class Solution {
 public:
  int largestPathValue(string colors, vector<vector<int>>& edges) {
    const int n = colors.length();
    int ans = 0;
    int processed = 0;
    vector<vector<int>> graph(n);
    vector<int> inDegrees(n);
    queue<int> q;
    vector<vector<int>> count(n, vector<int>(26));

    // Build the graph.
    for (const vector<int>& edge : edges) {
      const int u = edge[0];
      const int v = edge[1];
      graph[u].push_back(v);
      ++inDegrees[v];
    }

    // Perform topological sorting.
    for (int i = 0; i < n; ++i)
      if (inDegrees[i] == 0)
        q.push(i);

    while (!q.empty()) {
      const int out = q.front();
      q.pop();
      ++processed;
      ans = max(ans, ++count[out][colors[out] - 'a']);
      for (const int in : graph[out]) {
        for (int i = 0; i < 26; ++i)
          count[in][i] = max(count[in][i], count[out][i]);
        if (--inDegrees[in] == 0)
          q.push(in);
      }
    }

    return processed == n ? ans : -1;
  }
};


Get Biggest Three Rhombus Sums in a Grid

You are given an `m x n` integer matrix `grid`.

A rhombus sum is the sum of the elements that form the border of a regular
rhombus shape in `grid`. The rhombus must have the shape of a square
rotated 45 degrees with each of the corners centered in a grid cell. Below is
an image of four valid rhombus shapes with the corresponding colored cells
that should be included in each rhombus sum:

Note that the rhombus can have an area of 0, which is depicted by the purple
rhombus in the bottom right corner.

Return the biggest three distinct rhombus sums in the `grid` in descending
order. If there are less than three distinct values, return all of them.

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

from sortedcontainers import SortedSet

class Solution:
  def getBiggestThree(self, grid: List[List[int]]) -> List[int]:
    m = len(grid)
    n = len(grid[0])
    sums = SortedSet()

    for i in range(m):
      for j in range(n):
        sz = 0
        while i + sz < m and i - sz >= 0 and j + 2 * sz < n:
          summ = grid[i][j] if sz == 0 else self._getSum(grid, i, j, sz)
          sums.add(summ)
          if len(sums) > 3:
            sums.pop(0)
          sz += 1

    return reversed(sums)

  def _getSum(self, grid: List[List[int]], i: int, j: int, sz: int) -> int:
    x = i
    y = j
    summ = 0

    for _ in range(sz):
      x -= 1
      y += 1
      summ += grid[x][y]

    for _ in range(sz):
      x += 1
      y += 1
      summ += grid[x][y]

    for _ in range(sz):
      x += 1
      y -= 1
      summ += grid[x][y]

    for _ in range(sz):
      x -= 1
      y -= 1
      summ += grid[x][y]

    return summ

class Solution {
  public int[] getBiggestThree(int[][] grid) {
    final int m = grid.length;
    final int n = grid[0].length;
    TreeSet<Integer> sums = new TreeSet<>();

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        for (int sz = 0; i + sz < m && i - sz >= 0 && j + 2 * sz < n; ++sz) {
          final int sum = sz == 0 ? grid[i][j] : getSum(grid, i, j, sz);
          sums.add(sum);
          if (sums.size() > 3)
            sums.pollFirst();
        }

    return sums.descendingSet().stream().mapToInt(Integer::intValue).toArray();
  }

  // Returns the sum of the rhombus, where the top grid is (i, j) and the edge
  // size is `sz`.
  private int getSum(int[][] grid, int i, int j, int sz) {
    int x = i;
    int y = j;
    int sum = 0;

    // Go left down.
    for (int k = 0; k < sz; ++k)
      sum += grid[--x][++y];

    // Go right down.
    for (int k = 0; k < sz; ++k)
      sum += grid[++x][++y];

    // Go right up.
    for (int k = 0; k < sz; ++k)
      sum += grid[++x][--y];

    // Go left up.
    for (int k = 0; k < sz; ++k)
      sum += grid[--x][--y];

    return sum;
  }
}

class Solution {
 public:
  vector<int> getBiggestThree(vector<vector<int>>& grid) {
    const int m = grid.size();
    const int n = grid[0].size();
    set<int> sums;

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        for (int sz = 0; i + sz < m && i - sz >= 0 && j + 2 * sz < n; ++sz) {
          const int sum = sz == 0 ? grid[i][j] : getSum(grid, i, j, sz);
          sums.insert(sum);
          if (sums.size() > 3)
            sums.erase(sums.begin());
        }

    return vector<int>(sums.rbegin(), sums.rend());
  }

 private:
  // Returns the sum of the rhombus, where the top grid is (i, j) and the edge
  // size is `sz`.
  int getSum(const vector<vector<int>>& grid, int i, int j, int sz) {
    int x = i;
    int y = j;
    int sum = 0;

    // Go left down.
    for (int k = 0; k < sz; ++k)
      sum += grid[--x][++y];

    // Go right down.
    for (int k = 0; k < sz; ++k)
      sum += grid[++x][++y];

    // Go right up.
    for (int k = 0; k < sz; ++k)
      sum += grid[++x][--y];

    // Go left up.
    for (int k = 0; k < sz; ++k)
      sum += grid[--x][--y];

    return sum;
  }
};


Minimum Cost to Change the Final Value of Expression

You are given a valid boolean expression as a string `expression` consisting
of the characters `'1'`,`'0'`,`'&'` (bitwise AND operator),`'|'` (bitwise OR
operator),`'('`, and `')'`.

* For example, `"()1|1"` and `"(1)&()"` are not valid while `"1"`, `"(((1))|(0))"`, and `"1|(0&(1))"` are valid expressions.

Return the minimum cost to change the final value of the expression.

* For example, if `expression = "1|1|(0&0)&1"`, its value is `1|1|(0&0)&1 = 1|1|0&1 = 1|0&1 = 1&1 = 1`. We want to apply operations so that the new expression evaluates to `0`.

The cost of changing the final value of an expression is the number of
operations performed on the expression. The types of operations are described
as follows:

* Turn a `'1'` into a `'0'`.
* Turn a `'0'` into a `'1'`.
* Turn a `'&'` into a `'|'`.
* Turn a `'|'` into a `'&'`.

Note: `'&'` does not take precedence over `'|'` in the order of calculation.
Evaluate parentheses first, then in left-to-right order.

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 minOperationsToFlip(self, expression: str) -> int:
    stack = []

    for e in expression:
      if e in '(&|':
        stack.append((e, 0))
        continue
      if e == ')':
        lastPair = stack.pop()
        stack.pop()
      else:
        lastPair = (e, 1)
      if stack and stack[-1][0] in '&|':
        op = stack.pop()[0]
        a, costA = stack.pop()
        b, costB = lastPair
        if op == '&':
          if a == '0' and b == '0':
            lastPair = ('0', 1 + min(costA, costB))
          elif a == '0' and b == '1':
            lastPair = ('0', 1)
          elif a == '1' and b == '0':
            lastPair = ('0', 1)
          else:
            lastPair = ('1', min(costA, costB))
        else:
          if a == '0' and b == '0':
            lastPair = ('0', min(costA, costB))
          elif a == '0' and b == '1':
            lastPair = ('1', 1)
          elif a == '1' and b == '0':
            lastPair = ('1', 1)
          else:
            lastPair = ('1', 1 + min(costA, costB))
      stack.append(lastPair)

    return stack[-1][1]

class Solution {
  public int minOperationsToFlip(String expression) {
    // [(the expression, the cost to toggle the expression)]
    Deque<Pair<Character, Integer>> stack = new ArrayDeque<>();
    Pair<Character, Integer> lastPair = null;

    for (final char e : expression.toCharArray()) {
      if (e == '(' || e == '&' || e == '|') {
        // These aren't expressions, so the cost is meaningless.
        stack.push(new Pair<>(e, 0));
        continue;
      }
      if (e == ')') {
        lastPair = stack.pop();
        stack.pop(); // Pop '('.
      } else {       // e == '0' || e == '1'
        // Store the '0' or '1'. The cost to change their values is just 1,
        // whether it's changing '0' to '1' or '1' to '0'.
        lastPair = new Pair<>(e, 1);
      }
      if (!stack.isEmpty() && (stack.peek().getKey() == '&' || stack.peek().getKey() == '|')) {
        final char op = stack.pop().getKey();
        final char a = stack.peek().getKey();
        final int costA = stack.pop().getValue();
        final char b = lastPair.getKey();
        final int costB = lastPair.getValue();
        // Determine the cost to toggle op(a, b).
        if (op == '&') {
          if (a == '0' && b == '0')
            // Change '&' to '|' and a|b to '1'.
            lastPair = new Pair<>('0', 1 + Math.min(costA, costB));
          else if (a == '0' && b == '1')
            // Change '&' to '|'.
            lastPair = new Pair<>('0', 1);
          else if (a == '1' && b == '0')
            // Change '&' to '|'.
            lastPair = new Pair<>('0', 1);
          else // a == '1' and b == '1'
            // Change a|b to '0'.
            lastPair = new Pair<>('1', Math.min(costA, costB));
        } else { // op == '|'
          if (a == '0' && b == '0')
            // Change a|b to '1'.
            lastPair = new Pair<>('0', Math.min(costA, costB));
          else if (a == '0' && b == '1')
            // Change '|' to '&'.
            lastPair = new Pair<>('1', 1);
          else if (a == '1' && b == '0')
            // Change '|' to '&'.
            lastPair = new Pair<>('1', 1);
          else // a == '1' and b == '1'
            // Change '|' to '&' and a|b to '0'.
            lastPair = new Pair<>('1', 1 + Math.min(costA, costB));
        }
      }
      stack.push(lastPair);
    }

    return stack.peek().getValue();
  }
}

class Solution {
 public:
  int minOperationsToFlip(string expression) {
    // [(the expression, the cost to toggle the expression)]
    stack<pair<char, int>> stack;
    pair<char, int> lastPair;

    for (const char e : expression) {
      if (e == '(' || e == '&' || e == '|') {
        // These aren't expressions, so the cost is meaningless.
        stack.push({e, 0});
        continue;
      }
      if (e == ')') {
        lastPair = stack.top();
        stack.pop();
        stack.pop();  // Pop '('.
      } else {        // e == '0' || e == '1'
        // Store the '0' or '1'. The cost to change their values is just 1,
        // whether it's changing '0' to '1' or '1' to '0'.
        lastPair = {e, 1};
      }
      if (!stack.empty() &&
          (stack.top().first == '&' || stack.top().first == '|')) {
        const char op = stack.top().first;
        stack.pop();
        const auto [a, costA] = stack.top();
        stack.pop();
        const auto [b, costB] = lastPair;
        // Determine the cost to toggle op(a, b).
        if (op == '&') {
          if (a == '0' && b == '0')
            // Change '&' to '|' and a|b to '1'.
            lastPair = {'0', 1 + min(costA, costB)};
          else if (a == '0' && b == '1')
            // Change '&' to '|'.
            lastPair = {'0', 1};
          else if (a == '1' && b == '0')
            // Change '&' to '|'.
            lastPair = {'0', 1};
          else  // a == '1' and b == '1'
            // Change a|b to '0'.
            lastPair = {'1', min(costA, costB)};
        } else {  // op == '|'
          if (a == '0' && b == '0')
            // Change a|b to '1'.
            lastPair = {'0', min(costA, costB)};
          else if (a == '0' && b == '1')
            // Change '|' to '&'.
            lastPair = {'1', 1};
          else if (a == '1' && b == '0')
            // Change '|' to '&'.
            lastPair = {'1', 1};
          else  // a == '1' and b == '1'
            // Change '|' to '&' and a|b to '0'.
            lastPair = {'1', 1 + min(costA, costB)};
        }
      }
      stack.push(lastPair);
    }

    return stack.top().second;
  }
};


The minimum absolute difference of an array `a` is defined as the minimum
value of `|a[i] - a[j]|`, where `0 <= i < j < a.length` and `a[i] != a[j]`. If
all elements of `a` are the same, the minimum absolute difference is `-1`.

* For example, the minimum absolute difference of the array `[5,2,3,7,2]` is `|2 - 3| = 1`. Note that it is not `0` because `a[i]` and `a[j]` must be different.

You are given an integer array `nums` and the array `queries` where
`queries[i] = [li, ri]`. For each query `i`, compute the minimum absolute
difference of the subarray `nums[li...ri]` containing the elements of `nums`
between the 0-based indices `li` and `ri` (inclusive).

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

A subarray is a contiguous sequence of elements in an array.

The value of `|x|` is defined as:

* `x` if `x >= 0`.
* `-x` if `x < 0`.

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

class Solution:
  def minDifference(self, nums: List[int], queries: List[List[int]]) -> List[int]:
    numToIndices = [[] for _ in range(101)]

    for i, num in enumerate(nums):
      numToIndices[num].append(i)

    if len(numToIndices[nums[0]]) == len(nums):
      return [-1] * len(queries)

    ans = []

    for l, r in queries:
      prevNum = -1
      minDiff = 101
      for num in range(1, 101):
        indices = numToIndices[num]
        i = bisect_left(indices, l)
        if i == len(indices) or indices[i] > r:
          continue
        if prevNum != -1:
          minDiff = min(minDiff, num - prevNum)
        prevNum = num
      ans.append(-1 if minDiff == 101 else minDiff)

    return ans

class Solution {
  public int[] minDifference(int[] nums, int[][] queries) {
    int[] ans = new int[queries.length];
    List<Integer>[] numToIndices = new List[101];

    for (int i = 1; i <= 100; ++i)
      numToIndices[i] = new ArrayList<>();

    for (int i = 0; i < nums.length; ++i)
      numToIndices[nums[i]].add(i);

    if (numToIndices[nums[0]].size() == nums.length) {
      Arrays.fill(ans, -1);
      return ans;
    }

    for (int i = 0; i < queries.length; ++i) {
      final int l = queries[i][0];
      final int r = queries[i][1];
      int prevNum = -1;
      int minDiff = 101;
      for (int num = 1; num <= 100; ++num) {
        List<Integer> indices = numToIndices[num];
        final int j = firstGreaterEqual(indices, l);
        if (j == indices.size() || indices.get(j) > r)
          continue;
        if (prevNum != -1)
          minDiff = Math.min(minDiff, num - prevNum);
        prevNum = num;
      }
      ans[i] = minDiff == 101 ? -1 : minDiff;
    }

    return ans;
  }

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

class Solution {
 public:
  vector<int> minDifference(vector<int>& nums, vector<vector<int>>& queries) {
    vector<vector<int>> numToIndices(101);

    for (int i = 0; i < nums.size(); ++i)
      numToIndices[nums[i]].push_back(i);

    if (numToIndices[nums[0]].size() == nums.size())
      return vector<int>(queries.size(), -1);

    vector<int> ans;

    for (const vector<int>& query : queries) {
      const int l = query[0];
      const int r = query[1];
      int prevNum = -1;
      int minDiff = 101;
      for (int num = 1; num <= 100; ++num) {
        const auto& indices = numToIndices[num];
        const auto it = ranges::lower_bound(indices, l);
        if (it == indices.cend() || *it > r)
          continue;
        if (prevNum != -1)
          minDiff = min(minDiff, num - prevNum);
        prevNum = num;
      }
      ans.push_back(minDiff == 101 ? -1 : minDiff);
    }

    return ans;
  }
};


There is a country of `n` cities numbered from `0` to `n - 1`. In this
country, there is a road connecting every pair of cities.

There are `m` friends numbered from `0` to `m - 1` who are traveling through
the country. Each one of them will take a path consisting of some cities. Each
path is represented by an integer array that contains the visited cities in
order. The path may contain a city more than once, but the same city will not
be listed consecutively.

Given an integer `n` and a 2D integer array `paths` where `paths[i]` is an
integer array representing the path of the `ith` friend, return the length of
the longest common subpath that is shared by every friend's path, or `0` if
there is no common subpath at all.

A subpath of a path is a contiguous sequence of cities within that path.

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

class Solution:
  def __init__(self):
    self.kMod = 8_417_508_174_513
    self.kBase = 165_131

  def longestCommonSubpath(self, n: int, paths: List[List[int]]) -> int:
    l = 0
    r = len(paths[0])

    while l < r:
      m = l + (r - l + 1) // 2
      if self._checkCommonSubpath(paths, m):
        l = m
      else:
        r = m - 1

    return l

  def _checkCommonSubpath(self, paths: List[List[int]], m: int) -> bool:
    hashSets = [self._rabinKarp(path, m) for path in paths]

    for subpathHash in hashSets[0]:
      if all(subpathHash in hashSet for hashSet in hashSets):
        return True

    return False

  def _rabinKarp(self, path: List[int], m: int) -> Set[int]:
    hashes = set()
    maxPower = 1
    hash = 0

    for i, num in enumerate(path):
      hash = (hash * self.kBase + num) % self.kMod
      if i >= m:
        hash = (hash - path[i - m] * maxPower % self.kMod + self.kMod) % self.kMod
      else:
        maxPower = maxPower * self.kBase % self.kMod
      if i >= m - 1:
        hashes.add(hash)

    return hashes

class Solution {
  public int longestCommonSubpath(int n, int[][] paths) {
    int l = 0;
    int r = paths[0].length;

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

    return l;
  }

  private static final long BASE = 165_131L;
  private static final long HASH = 8_417_508_174_513L;

  // Returns true if there's a common subpath of length m for all the paths.
  private boolean checkCommonSubpath(int[][] paths, int m) {
    Set<Long>[] hashSets = new Set[paths.length];

    // Calculate the hash values for subpaths of length m for every path.
    for (int i = 0; i < paths.length; ++i)
      hashSets[i] = rabinKarp(paths[i], m);

    // Check if there is a common subpath of length m.
    for (final long subpathHash : hashSets[0])
      if (Arrays.stream(hashSets).allMatch(hashSet -> hashSet.contains(subpathHash)))
        return true;

    return false;
  }

  // Returns the hash values for subpaths of length m in the path.
  private Set<Long> rabinKarp(int[] path, int m) {
    Set<Long> hashes = new HashSet<>();
    long maxPower = 1;
    long hash = 0;
    for (int i = 0; i < path.length; ++i) {
      hash = (hash * BASE + path[i]) % HASH;
      if (i >= m)
        hash = (hash - path[i - m] * maxPower % HASH + HASH) % HASH;
      else
        maxPower = maxPower * BASE % HASH;
      if (i >= m - 1)
        hashes.add(hash);
    }
    return hashes;
  }
}

class Solution {
 public:
  int longestCommonSubpath(int n, vector<vector<int>>& paths) {
    int l = 0;
    int r = paths[0].size();

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

    return l;
  }

  static constexpr long kBase = 165'131;
  static constexpr long kHash = 8'417'508'174'513;

  // Returns true if there's a common subpath of length m for all the paths.
  bool checkCommonSubpath(const vector<vector<int>>& paths, int m) {
    vector<unordered_set<long>> hashSets;

    // Calculate the hash values for subpaths of length m for every path.
    for (const vector<int>& path : paths)
      hashSets.push_back(rabinKarp(path, m));

    // Check if there is a common subpath of length m.
    for (const long subpathHash : hashSets[0])
      if (ranges::all_of(hashSets,
                         [subpathHash](const unordered_set<long>& hashSet) {
        return hashSet.contains(subpathHash);
      }))
        return true;

    return false;
  }

  // Returns the hash values for subpaths of length m in the path.
  unordered_set<long> rabinKarp(const vector<int>& path, int m) {
    unordered_set<long> hashes;
    long maxPower = 1;
    long hash = 0;
    for (int i = 0; i < path.size(); ++i) {
      hash = (hash * kBase + path[i]) % kHash;
      if (i >= m)
        hash = (hash - path[i - m] * maxPower % kHash + kHash) % kHash;
      else
        maxPower = maxPower * kBase % kHash;
      if (i >= m - 1)
        hashes.insert(hash);
    }
    return hashes;
  }
};