LC 8026. 构造乘积矩阵

8026. 构造乘积矩阵 - 力扣(LeetCode)

这题又有点想偏了。

最开始的想法就是使用类似费马小定理的方法来解决,但是后来发现其实不行,因为费马小定理要求的是质数。虽然12345不是质数,但是它可以分解成为 `12345 = 3 x 5 x 823`. 我们可以先对所有的数进行因式分解,计算3,5,823分别有多少个,然后对于剩余下数就需要计算 `(b^-1) % MOD`. 不过这里不能使用费马小定理,因为它要求MOD是质数。但是我问了ChatGPT,如果MOD不是质数,但是b如果和MOD互质的话,那么其实可以使用扩展欧几里得算法得到 b 的逆元。

这里解法就是 `b * x = 1(% MOD)`, 我们其实可以写成 `b * x + MOD * y = 1(%MOD)`. 然后计算出x. 那么x就是 `(b^-1) % MOD`.

代码如下,整体运行时间在2s左右,题解中给出的前后缀算法只需要200ms左右。

def pow_mod(a, b, MOD):
    res = 1
    while b:
        if b & 0x1:
            res = (res * a) % MOD
        a = (a * a) % MOD
        b = b >> 1
    return res


def extended_gcd(a, b):
    if b == 0:
        return a, 1, 0
    else:
        d, x1, y1 = extended_gcd(b, a % b)
        x = y1
        y = x1 - (a // b) * y1
        return d, x, y


# 相比费马小定理,这里不要求MOD是质数,只需要确保b和MOD是互质就行
# b * x + MOD * y =  1(% MOD)
def mod_inverse(b, MOD):
    d, x, y = extended_gcd(b, MOD)
    assert (d == 1)
    return x % MOD


class Solution:
    def constructProductMatrix(self, grid: List[List[int]]) -> List[List[int]]:
        n, m = len(grid), len(grid[0])
        # 12345 = 3,5,823
        MOD = 12345
        A, B, C, D = 0, 0, 0, 1
        encode = {}
        for i in range(n):
            for j in range(m):
                a, b, c = 0, 0, 0
                x = grid[i][j]
                while x % 3 == 0:
                    a += 1
                    x = x // 3
                while x % 5 == 0:
                    b += 1
                    x = x // 5
                while x % 823 == 0:
                    c += 1
                    x = x // 823
                encode[(i, j)] = (a, b, c, x)
                A, B, C = A + a, B + b, C + c
                D = (D * x) % MOD

        # print(A, B, C, D)
        ans = [[0] * m for _ in range(n)]
        for i in range(n):
            for j in range(m):
                a, b, c, d = encode[(i, j)]
                # (D/d) % MOD
                # D % MOD * (d^(-1) % MOD)
                v = D * mod_inverse(d, MOD)
                v = (v * pow_mod(3, (A - a), MOD)) % MOD
                v = (v * pow_mod(5, (B - b), MOD)) % MOD
                v = (v * pow_mod(823, (C - c), MOD)) % MOD
                ans[i][j] = v
        return ans

题解给出的前后缀算法更加简单,只需要计算前缀乘积和后缀乘积就行,这里面就没有啥特别的数论算法了。

class Solution:
    def constructProductMatrix(self, grid: List[List[int]]) -> List[List[int]]:
        n, m = len(grid), len(grid[0])
        ans = [[0] * m for _ in range(n)]
        prev = 1
        MOD = 12345
        for i in range(n):
            for j in range(m):
                ans[i][j] = prev
                prev = prev * grid[i][j]
                prev = prev % MOD
        prev = 1
        for i in reversed(range(n)):
            for j in reversed(range(m)):
                ans[i][j] = (ans[i][j] * prev) % MOD
                prev = prev * grid[i][j]
                prev = prev % MOD
        return ans