LC 8026. 构造乘积矩阵
这题又有点想偏了。
最开始的想法就是使用类似费马小定理的方法来解决,但是后来发现其实不行,因为费马小定理要求的是质数。虽然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