以下是 LeetCode 3725 的 Python3 实现,使用容斥原理 + DP,思路与 Java 版本一致:

```python
from typing import List

class Solution:
    def countCoprime(self, mat: List[List[int]]) -> int:
        MOD = 10**9 + 7
        MAX_VAL = 150
        
        # 1. 预处理每个数的所有因子
        divisors = [[] for _ in range(MAX_VAL + 1)]
        for d in range(1, MAX_VAL + 1):
            for multiple in range(d, MAX_VAL + 1, d):
                divisors[multiple].append(d)
        
        m = len(mat)
        # 2. 统计每行中,能被 d 整除的数字个数
        row_divisor_cnt = [[0] * (MAX_VAL + 1) for _ in range(m)]
        for i in range(m):
            for x in mat[i]:
                for d in divisors[x]:
                    row_divisor_cnt[i][d] += 1
        
        # 3. 容斥:计算"gcd恰好为d"的方案数
        exact_gcd = [0] * (MAX_VAL + 1)
        for d in range(MAX_VAL, 0, -1):
            ways = 1
            for i in range(m):
                ways = (ways * row_divisor_cnt[i][d]) % MOD
                if ways == 0:
                    break
            # 减去 gcd 是 d 的倍数的方案
            for multiple in range(2 * d, MAX_VAL + 1, d):
                ways = (ways - exact_gcd[multiple]) % MOD
            exact_gcd[d] = ways
        
        return exact_gcd[1] % MOD
```

核心思路说明

1. 正向计算:row_divisor_cnt[i][d] 表示第 i 行中能被 d 整除的数字个数。那么所有行都选 d 的倍数的方案数为 ∏ row_divisor_cnt[i][d]。
2. 容斥去重:
   · ∏ row_divisor_cnt[i][d] 实际上统计的是 gcd 为 d、2d、3d... 的所有方案
   · 从大到小遍历 d,用 ways 减去所有 exact_gcd[multiple](multiple 是 d 的倍数),剩下的就是 gcd 恰好为 d 的方案数
3. 为什么从大到小:因为计算 exact_gcd[d] 需要用到 exact_gcd[2d]、exact_gcd[3d] 等更大的数,所以必须逆序计算。

复杂度分析

· 时间复杂度:O(m * n * τ + V²),其中 V=150,τ 是每个数的因子个数(≤12)
· 空间复杂度:O(m * V)

 

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐