
2026-03-17:统计每一行选择互质整数的方案数。用go语言,给定一个由正整数构成的矩阵,尺寸为 m × n。
从矩阵的每一行中各选取一个数,得到 m 个被选数字。
统计这样的选法数量,使得这些被选数字的最大公约数为 1。
由于结果可能很大,答案需对 1000000007 取模后返回。
1 <= m == mat.length <= 150。
1 <= n == mat[i].length <= 150。
1 <= mat[i][j] <= 150。
输入: mat = [[1,2],[3,4]]。
输出: 3。
解释:
第一行中选择的整数 | 第二行中选择的整数 | 被选整数的最大公约数 |
|---|---|---|
1 | 3 | 1 |
1 | 4 | 1 |
2 | 3 | 1 |
2 | 4 | 2 |
其中 3 种组合的最大公约数为 1。因此,答案是 3。
题目来自力扣3725。
直接统计gcd=1的方案很难,我们用逆向思维:
因为矩阵元素最大值是150,提前计算好 1 到 150 每个数字的所有正因子,存入数组备用。
对矩阵的每一行单独处理,统计这一行里:能被 d 整除的数字有多少个(d 是所有可能的因子)。
因子-计数数组(表示该行有多少个数能被这个因子整除)。示例: 第一行 [1,2]:
根据乘法原理:从每行选1个能被d整除的数,总方案数 = 每行的可选数量相乘。
示例: d=1:第一行可选2个,第二行可选2个 → 总方案=2×2=4 d=2:第一行可选1个,第二行可选1个 → 总方案=1×1=1 d=3:第一行可选0个 → 总方案=0 d=4:第一行可选0个 → 总方案=0
这是核心步骤:我们之前算的是gcd是d的倍数的方案数,需要剔除gcd更大的方案,得到恰好gcd=d的方案数。
示例:
恰好gcd=1的方案数就是答案,为了保证结果非负,最终再取一次模。
我们拆分所有步骤的耗时:
O(maxVal² + m×n×maxVal) 代入题目限制(m,n,maxVal≤150),总操作数约 340万,效率极高。
额外空间指除输入矩阵外开辟的所有存储空间:
O(m×maxVal + maxVal) 代入限制,空间占用极小,完全满足题目要求。
.
package main
import (
"fmt"
"slices"
)
const maxVal = 151
var divisors [maxVal][]int
func init() {
for i := 1; i < maxVal; i++ {
for j := i; j < maxVal; j += i {
divisors[j] = append(divisors[j], i)
}
}
}
func countCoprime(mat [][]int)int {
const mod = 1_000_000_007
// 预处理每行的因子个数
divisorCnt := make([][]int, len(mat))
mx := 0
for i, row := range mat {
rowMax := slices.Max(row)
mx = max(mx, rowMax)
divisorCnt[i] = make([]int, rowMax+1)
for _, x := range row {
for _, d := range divisors[x] {
divisorCnt[i][d]++
}
}
}
cntGcd := make([]int, mx+1)
for i := mx; i > 0; i-- {
// 每行选一个 i 的倍数的方案数
res := 1
for _, cnt := range divisorCnt {
if i >= len(cnt) || cnt[i] == 0 {
res = 0
break
}
res = res * cnt[i] % mod // 乘法原理
}
for j := i; j <= mx; j += i {
res -= cntGcd[j] // 注意这里有减法,可能导致 res 是负数
}
cntGcd[i] = res % mod
}
return (cntGcd[1] + mod) % mod // 保证结果非负
}
func main() {
mat := [][]int{{1, 2}, {3, 4}}
result := countCoprime(mat)
fmt.Println(result)
}

.
# -*-coding:utf-8-*-
MOD = 1_000_000_007
MAX_VAL = 151
# 预处理每个数的因子
divisors = [[] for _ in range(MAX_VAL)]
for i in range(1, MAX_VAL):
for j in range(i, MAX_VAL, i):
divisors[j].append(i)
def count_coprime(mat):
# 预处理每行的因子计数
divisor_cnt = []
mx = 0
for row in mat:
row_max = max(row)
mx = max(mx, row_max)
# 创建当前行的计数数组,长度为 row_max + 1
row_cnt = [0] * (row_max + 1)
for x in row:
for d in divisors[x]:
if d <= row_max:
row_cnt[d] += 1
divisor_cnt.append(row_cnt)
cnt_gcd = [0] * (mx + 1)
# 从大到小计算
for i in range(mx, 0, -1):
# 每行选一个 i 的倍数的方案数
res = 1
for row_cnt in divisor_cnt:
if i >= len(row_cnt) or row_cnt[i] == 0:
res = 0
break
res = (res * row_cnt[i]) % MOD
# 减去 gcd 大于 i 的方案数
for j in range(i * 2, mx + 1, i):
res = (res - cnt_gcd[j]) % MOD
cnt_gcd[i] = res
# 确保结果非负
return (cnt_gcd[1] + MOD) % MOD
def main():
mat = [[1, 2], [3, 4]]
result = count_coprime(mat)
print(result)
if __name__ == "__main__":
main()
.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAX_VAL = 151;
const int MOD = 1'000'000'007;
vector<int> divisors[MAX_VAL];
// 初始化函数,预计算每个数的因子
void initDivisors() {
for (int i = 1; i < MAX_VAL; i++) {
for (int j = i; j < MAX_VAL; j += i) {
divisors[j].push_back(i);
}
}
}
int countCoprime(vector<vector<int>>& mat) {
// 预处理每行的因子个数
vector<vector<int>> divisorCnt(mat.size());
int mx = 0;
for (int i = 0; i < mat.size(); i++) {
int rowMax = *max_element(mat[i].begin(), mat[i].end());
mx = max(mx, rowMax);
divisorCnt[i].resize(rowMax + 1, 0);
for (int x : mat[i]) {
for (int d : divisors[x]) {
divisorCnt[i][d]++;
}
}
}
vector<int> cntGcd(mx + 1, 0);
// 从大到小计算
for (int i = mx; i > 0; i--) {
// 每行选一个 i 的倍数的方案数
long long res = 1;
for (auto& cnt : divisorCnt) {
if (i >= cnt.size() || cnt[i] == 0) {
res = 0;
break;
}
res = (res * cnt[i]) % MOD;
}
// 减去 gcd 大于 i 的方案数
for (int j = i * 2; j <= mx; j += i) {
res = (res - cntGcd[j] + MOD) % MOD;
}
cntGcd[i] = res % MOD;
}
return (cntGcd[1] + MOD) % MOD;
}
int main() {
// 初始化因子表
initDivisors();
vector<vector<int>> mat = {{1, 2}, {3, 4}};
int result = countCoprime(mat);
cout << result << endl;
return 0;
}

·
我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。 欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。
·