|
| 1 | +# [Problem 2906: Construct Product Matrix](https://leetcode.com/problems/construct-product-matrix/description/?envType=daily-question) |
| 2 | + |
| 3 | +## Initial thoughts (stream-of-consciousness) |
| 4 | +We need, for each cell p[i][j], the product of all grid elements except grid[i][j], modulo 12345. A naive approach is to compute the product of all elements then divide by grid[i][j] modulo 12345. But modulo 12345 is composite (12345 = 3 * 5 * 823) so modular inverse of grid[i][j] doesn't always exist. Also the true product of all elements (without modulo) would be astronomically large (up to 1e5 numbers of up to 1e9), so we can't compute it directly in full integer form. |
| 5 | + |
| 6 | +Observation: because the modulus M = 12345 factors into small primes (3, 5, 823), we can separate each element into the part composed of these prime factors and the remaining part that is coprime to M. If we record, for each element, the exponent counts for the primes {3,5,823} and the "remainder" after removing these primes, then: |
| 7 | +- The product of all remainders is coprime to M, so we can compute its modular inverse per element reliably (after removing that element's remainder). |
| 8 | +- The product of primes can be handled by tracking total exponents and subtracting the exponent of the excluded element. |
| 9 | + |
| 10 | +This avoids needing inverses for values that share factors with M and works with modular arithmetic efficiently. |
| 11 | + |
| 12 | +## Refining the problem, round 2 thoughts |
| 13 | +Plan: |
| 14 | +- Factor M = 12345 into primes [3, 5, 823]. |
| 15 | +- For each grid value a: |
| 16 | + - For each prime p in [3,5,823], count how many times p divides a (e_p) and divide it out; store e_p for that element. |
| 17 | + - After removing those factors, what's left (rem) is coprime to M; store rem. |
| 18 | +- Maintain total exponent sums E_p for each prime across all elements and rem_prod = product of all rem modulo M. |
| 19 | +- For an element a_i, the product of all other elements modulo M equals: |
| 20 | + (rem_prod * inv(rem_i) mod M) * product_over_primes p^(E_p - e_p_i) mod M |
| 21 | + where inv(rem_i) is the modular inverse of rem_i modulo M (exists because rem_i is coprime to M). |
| 22 | +- Compute extended gcd based modular inverses for rem_i (or cache inverses for repeated rem values to optimize). |
| 23 | +- Reconstruct the answer matrix shape-wise. |
| 24 | + |
| 25 | +Complexity: |
| 26 | +- Let N = n*m (<= 1e5). For each element we perform division by at most three primes repeatedly — small cost. Also computing modular inverse (extended gcd) and a few modular exponentiations per element. So overall time O(N * log M + N * small_division_cost). Space O(N) to store exponents/remainders (or we could stream and keep them in arrays). |
| 27 | + |
| 28 | +Edge cases: |
| 29 | +- Elements equal to 1 (no prime factors) — handled naturally. |
| 30 | +- Elements divisible entirely by M (e.g., 12345) — they will have exponents for all primes, rem becomes 1; the formula still works and may produce 0 modulo M for some outputs. |
| 31 | +- No zeros in input (given grid values >= 1), so we don't have to handle zero specially. |
| 32 | + |
| 33 | +## Attempted solution(s) |
| 34 | +```python |
| 35 | +from typing import List, Tuple |
| 36 | + |
| 37 | +class Solution: |
| 38 | + def productExceptSelf(self, grid: List[List[int]]) -> List[List[int]]: |
| 39 | + M = 12345 |
| 40 | + primes = [3, 5, 823] |
| 41 | + |
| 42 | + # Flatten grid to process easily and map back |
| 43 | + n = len(grid) |
| 44 | + m = len(grid[0]) |
| 45 | + flat = [] |
| 46 | + for row in grid: |
| 47 | + flat.extend(row) |
| 48 | + N = len(flat) |
| 49 | + |
| 50 | + # Store exponent counts for each prime for each element and the remainder after removing those primes |
| 51 | + exps = [[0,0,0] for _ in range(N)] |
| 52 | + rems = [0] * N |
| 53 | + |
| 54 | + # Totals |
| 55 | + total_exp = [0,0,0] |
| 56 | + rem_prod = 1 |
| 57 | + |
| 58 | + for i, val in enumerate(flat): |
| 59 | + x = val |
| 60 | + for pi, p in enumerate(primes): |
| 61 | + cnt = 0 |
| 62 | + while x % p == 0: |
| 63 | + x //= p |
| 64 | + cnt += 1 |
| 65 | + exps[i][pi] = cnt |
| 66 | + total_exp[pi] += cnt |
| 67 | + rems[i] = x # x is now coprime with M (we removed factors 3,5,823) |
| 68 | + rem_prod = (rem_prod * (x % M)) % M |
| 69 | + |
| 70 | + # extended gcd for modular inverse |
| 71 | + def egcd(a: int, b: int) -> Tuple[int,int,int]: |
| 72 | + if b == 0: |
| 73 | + return a, 1, 0 |
| 74 | + g, x1, y1 = egcd(b, a % b) |
| 75 | + # x1, y1 correspond to solution for b and a % b; update to a,b |
| 76 | + x = y1 |
| 77 | + y = x1 - (a // b) * y1 |
| 78 | + return g, x, y |
| 79 | + |
| 80 | + def modinv(a: int, mod: int) -> int: |
| 81 | + g, x, _ = egcd(a, mod) |
| 82 | + if g != 1: |
| 83 | + # inverse doesn't exist, but by construction a should be coprime to mod |
| 84 | + raise ValueError("No modular inverse") |
| 85 | + return x % mod |
| 86 | + |
| 87 | + # Optional: cache inverses for repeated remainders to speed up |
| 88 | + inv_cache = {} |
| 89 | + |
| 90 | + # Build answer flattened, then reshape |
| 91 | + ans_flat = [0] * N |
| 92 | + for i in range(N): |
| 93 | + rem_i = rems[i] % M |
| 94 | + if rem_i not in inv_cache: |
| 95 | + inv_cache[rem_i] = modinv(rem_i, M) |
| 96 | + inv_rem_i = inv_cache[rem_i] |
| 97 | + |
| 98 | + # start with rem_prod / rem_i (mod M) |
| 99 | + base = rem_prod * inv_rem_i % M |
| 100 | + |
| 101 | + # multiply by prime powers p^(total_exp - exps[i]) |
| 102 | + val = base |
| 103 | + for pi, p in enumerate(primes): |
| 104 | + exp_need = total_exp[pi] - exps[i][pi] |
| 105 | + if exp_need > 0: |
| 106 | + val = (val * pow(p, exp_need, M)) % M |
| 107 | + # if exp_need == 0 nothing to do; pow(...,0) == 1 |
| 108 | + ans_flat[i] = val |
| 109 | + |
| 110 | + # reshape to n x m |
| 111 | + out = [] |
| 112 | + idx = 0 |
| 113 | + for _ in range(n): |
| 114 | + out.append(ans_flat[idx: idx + m]) |
| 115 | + idx += m |
| 116 | + return out |
| 117 | +``` |
| 118 | +- Notes: |
| 119 | + - We factor modulus M = 12345 into primes [3, 5, 823] and remove those factors from each element. |
| 120 | + - rems[i] is coprime to M, so modular inverse exists; we compute inv(rem_i) using extended gcd. |
| 121 | + - We keep total exponents per prime across all elements; for element i we use exponent total_exp - exps[i] to get the product of primes excluding element i. |
| 122 | + - Time complexity: O(N * (small constant factor for dividing out primes + log M for modexp and extended gcd)). For N <= 1e5 this is efficient. |
| 123 | + - Space complexity: O(N) extra for storing exponents and remainders (could be optimized to streaming but it's fine here). |
0 commit comments