# Count Ways to Make Array With Product in Java

The problem of counting ways to form products in 2D arrays is a combinatorial mathematics challenge. It involves determining the number of ways to arrange positive integers within a 2D array, using modular arithmetic to prevent overflow and ensure computational stability.

Example 1:

Input: queries = [[2,6],[5,1],[73,660]]

Output: [4,1,50734910]

Explanation: Each query is self-sufficient

[2,6]: There are four ways to fill an array of size two that multiply to six. [1,6], [2,3], [3,2], [6,1].

[5,1]: There is one way to fill a 5 x 1 array: [1,1,1,1,1].

[73,660]: There are 1050734917 ways to fill a 73-by-660 array. 1050734917 modulo 109 + 7 equals 50734910.

Example 2:

Input: queries = [[1,1],[2,2],[3,3],[4,4],[5,5]]

Output: [1,2,3,10,5]

Explanation:

Query [1, 1]: Output: 1

Query [2, 2]: Output: 2

Query [3, 3]: Output: 3

Query [4, 4]: Output: 10

Query [5, 5]: Output: 5

output array corresponds to the given input queries, representing the number of ways to fill each array.

## Using Optimized Approach

### Algorithm

Step 1: Takes an array of input queries containing two integers: `n` and `k`.
Step 2: Computes the number of ways to fill the array for each query and stores the results in an array.
Step 3: Computes the number of ways to fill the array using prime factorization.
Step 4: Finds the prime index and calculates the divisor for a given number `k`.
Step 5: Computes and caches the counts array for a given `n`, using dynamic programming for efficient calculation.
Step 6: Includes precomputed arrays for inverses and prime numbers for faster calculations.

Here's an implementation of counting ways to make an array with product using an optimized approach :

FileName: ArrayFillWaysSolver.java

`import java.util.Arrays;public class ArrayFillWaysSolver {    static final long MOD = 1000000007;    static final int MAX = 10000;    // Arrays to store divisors and counts    static final int[] divisors = new int[MAX + 1];    static final int[] countsArray = new int[MAX + 1];    public static void main(String[] args) {        int[][] inputQueries = {{1,1},{2,2},{3,3},{4,4},{5,5}};        ArrayFillWaysSolver solver = new ArrayFillWaysSolver();        int[] result = solver.calculateArrayFillWays(inputQueries);        System.out.println(Arrays.toString(result));    }    // Method to calculate array fill ways for multiple queries    public int[] calculateArrayFillWays(int[][] queries) {        int[] results = new int[queries.length];        for (int i = 0; i < queries.length; i++) {            // Calculate array fill ways for each query            results[i] = computeWaysToFillArray(queries[i][0], queries[i][1]);        }        return results;    }    // Method to compute array fill ways for a single query    static int computeWaysToFillArray(int n, int k) {        long[] counts = getCounts(n);        long result = 1;        int primeIndex = 0;        while (k > 1) {            if (divisors[k] == 0) primeIndex = findPrimeIndex(primeIndex, k);            result = (result * counts[countsArray[k]]) % MOD;            k /= divisors[k];        }        return (int) result;    }    // Method to find the prime index and calculate divisor    static int findPrimeIndex(final int primeIndex, final int k) {        for (int i = primeIndex; i < primes.length; i++) {            final int prime = primes[i];            if (k % prime == 0) {                int divisor = prime * prime;                int count = 1;                while (k % divisor == 0) {                    count++;                    divisor *= prime;                }                // Store count and divisor                countsArray[k] = count;                divisors[k] = divisor / prime;                return primeIndex + 1;            }        }        divisors[k] = k;        countsArray[k] = 1;        return primes.length;    }    // Cache to store counts arrays    static long[][] cache = new long[MAX + 1][];    // Method to get counts array for a given n    private static long[] getCounts(int n) {        if (cache[n] != null) return cache[n];        final long[] counts = new long[14];        counts[0] = 1;        counts[1] = n;        counts[2] = n * (n + 1) / 2;        for (int i = 3; i < counts.length; i++) {            counts[i] = (((counts[i - 1] * (i + n - 1)) % MOD) * inverses[i - 1]) % MOD;        }        return cache[n] = counts;    }    // Precomputed inverses array for faster calculations    static long[] inverses = new long[] {1, 500000004, 333333336, 250000002, 400000003, 166666668, 142857144, 125000001, 111111112, 700000005, 818181824, 83333334, 153846155, 71428572};    // Precomputed primes array for finding divisors    static int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,            79, 83, 89, 97};}`

Output:

`[1, 2, 3, 10, 5]`

Complexity analysis:  The time complexity is O(Q * log(K)), dominated by loop iterations and the calculation of array fill ways. The space complexity is O(MAX) due to the storage of arrays and caches. The overall time complexity is reasonable given the problem constraints, but the space complexity might be large for larger values of MAX. However, it is generally efficient for typical problem instances.