Problem: Count Ways to Make Array With Product

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.


Python solution:
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


Java solution:
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);
  }
}


C++ solution:
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;
  }
};