2025-05-25:最大公约数相等的子序列数量。用go语言,给定一个整数数组 nums,需要计算满足以下条件的非空子序列对 (
【摘要】 2025-05-25:最大公约数相等的子序列数量。用go语言,给定一个整数数组 nums,需要计算满足以下条件的非空子序列对 (seq1, seq2) 的数量:seq1 和 seq2 是 nums 的两个非空子序列,且它们在原数组中的索引不重叠(即它们没有共同的元素下标)。seq1 中所有元素的最大公约数(GCD)与 seq2 中所有元素的最大公约数相等。求满足上述条件的子序列对总数,并将结...
2025-05-25:最大公约数相等的子序列数量。用go语言,给定一个整数数组 nums,需要计算满足以下条件的非空子序列对 (seq1, seq2) 的数量:
-
seq1 和 seq2 是 nums 的两个非空子序列,且它们在原数组中的索引不重叠(即它们没有共同的元素下标)。
-
seq1 中所有元素的最大公约数(GCD)与 seq2 中所有元素的最大公约数相等。
求满足上述条件的子序列对总数,并将结果对 1000000007 取模后返回。
1 <= nums.length <= 200。
1 <= nums[i] <= 200。
输入: nums = [10,20,30]。
输出: 2。
解释:
元素 GCD 等于 10 的子序列对有:
([10, 20, 30]的10, [10, 20, 30])的20,30。
([10, 20, 30]的20,30, [10, 20, 30])的10。
题目来自力扣3336。
解决思路
1. 预处理
- LCM 表:预先计算
lcms[i][j]表示i和j的最小公倍数(LCM),用于后续计算。 - 幂数组:预先计算
pow2[i]和pow3[i],分别表示2^i和3^i对mod取模的结果,用于快速计算子序列的数量。 - Möbius 函数:预先计算
mu[i],用于倍数容斥(Möbius inversion)以消除重复计数。
2. 统计倍数数量
- 计数数组
cnt:cnt[i]表示nums中i的倍数的个数。通过遍历nums并统计每个数的倍数,填充cnt数组。
3. 计算子序列对
- 子序列对贡献
f[g1][g2]:对于所有可能的g1和g2,计算满足gcd(seq1) = g1和gcd(seq2) = g2的子序列对的数量。l是g1和g2的最小公倍数(lcm(g1, g2))。c是nums中l的倍数的数量(即同时是g1和g2的倍数的元素数量)。c1和c2分别是nums中g1和g2的倍数的数量。- 公式
(pow3[c] * pow2[c1 + c2 - 2*c] - pow2[c1] - pow2[c2] + 1) % mod计算满足条件的子序列对数量:pow3[c]:seq1和seq2可以同时选择l的倍数的元素(每个元素可以出现在seq1、seq2或都不出现)。pow2[c1 + c2 - 2*c]:seq1和seq2只能选择非l倍数的g1或g2的倍数。- 减去
pow2[c1]和pow2[c2]是为了排除seq2或seq1为空的情况。 +1是为了修正减去的重复部分。
4. 倍数容斥
- 使用 Möbius 函数
mu进行容斥,避免重复计算。对于所有i,遍历j和k的倍数,累加mu[j] * mu[k] * f[j*i][k*i]到答案中。 - 最终答案需要对
mod取模并调整为非负数。
时间复杂度
- 预处理:
- LCM 表:
O(mx^2 * log(mx))(mx是nums的最大值,这里是 200)。 - 幂数组:
O(mx)。 - Möbius 函数:
O(mx log(mx))(类似筛法)。
- LCM 表:
- 统计倍数数量:
- 遍历
nums:O(n)。 - 填充
cnt:O(mx log(mx))(每个数的倍数)。
- 遍历
- 计算
f[g1][g2]:- 双重循环:
O(mx^2)。 - 每次计算
f[g1][g2]是O(1)。
- 双重循环:
- 倍数容斥:
- 三重循环:
O(mx * (mx/i)^2),最坏情况下是O(mx^3)。
- 三重循环:
总时间复杂度:O(mx^3),其中 mx = 200,因此实际计算量约为 200^3 = 8,000,000。
空间复杂度
- LCM 表:
O(mx^2)。 - 幂数组和 Möbius 函数:
O(mx)。 - 计数数组
cnt:O(mx)。 f数组:O(mx^2)。
总空间复杂度:O(mx^2),即 O(200^2) = 40,000。
Go完整代码如下:
package main
import (
"fmt"
"slices"
)
const mod = 1_000_000_007
const mx = 201
var lcms [mx][mx]int
var pow2, pow3, mu [mx]int
func init() {
for i := 1; i < mx; i++ {
for j := 1; j < mx; j++ {
lcms[i][j] = lcm(i, j)
}
}
pow2[0], pow3[0] = 1, 1
for i := 1; i < mx; i++ {
pow2[i] = pow2[i-1] * 2 % mod
pow3[i] = pow3[i-1] * 3 % mod
}
mu[1] = 1
for i := 1; i < mx; i++ {
for j := i * 2; j < mx; j += i {
mu[j] -= mu[i]
}
}
}
func subsequencePairCount(nums []int) int {
m := slices.Max(nums)
// cnt[i] 表示 nums 中的 i 的倍数的个数
cnt := make([]int, m+1)
for _, x := range nums {
cnt[x]++
}
for i := 1; i <= m; i++ {
for j := i * 2; j <= m; j += i {
cnt[i] += cnt[j] // 统计 i 的倍数的个数
}
}
f := make([][]int, m+1)
for g1 := 1; g1 <= m; g1++ {
f[g1] = make([]int, m+1)
for g2 := 1; g2 <= m; g2++ {
l := lcms[g1][g2]
c := 0
if l <= m {
c = cnt[l]
}
c1, c2 := cnt[g1], cnt[g2]
f[g1][g2] = (pow3[c]*pow2[c1+c2-c*2] - pow2[c1] - pow2[c2] + 1) % mod
}
}
// 倍数容斥
ans := 0
for i := 1; i <= m; i++ {
for j := 1; j <= m/i; j++ {
for k := 1; k <= m/i; k++ {
ans += mu[j] * mu[k] * f[j*i][k*i]
}
}
}
return (ans%mod + mod) % mod // 保证 ans 非负
}
func gcd(a, b int) int {
for a != 0 {
a, b = b%a, a
}
return b
}
func lcm(a, b int) int {
return a / gcd(a, b) * b
}
func main() {
nums := []int{10,20,30}
result := subsequencePairCount(nums)
fmt.Println(result)
}

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)