法一
力扣上大多数题解都是法一。因为值域是2e5,又是和公约数有关,所以肯定会考虑枚举因数。这里就考虑枚举i
,并判定是否存在一个子序列的gcd是i
。
开桶c[]
记录每个数出现次数,枚举i
的所有倍数j*i
,则sum(c[j*i])
就找到了数组里i
的倍数构成的集合。对于每个合法j
,求它们的gcd。若求得gcd为1,则说明该集合的gcd就是i
,则i
对答案有1的贡献;否则i
对答案无贡献。
时间复杂度O(n*logn*logn)
。
法二
这位神犇的解法是法二。定义c[i]
为数组中是i
的倍数的数字个数。对于i
的倍数j*i,j >= 2
,因为j*i
的倍数的集合一定是i
的倍数的集合的子集,所以总是满足c[j*i] <= c[i]
。对于所有c[j*i] == c[i]
的j
,我们应只保留最大的那个j
。比如[6,12,24,36]
这个集合,c[6*1] == c[3*1] == c[2*1] == c[1] == 4
,则只保留j = 6
即可。
实现以上想法时,则枚举所有倍数,统计那些c[i]
严格大于所有c[j*i]
的i
。
时间复杂度O(n*logn)
。但是实测跑得比法一慢。
代码
法一
"use strict";
var countDifferentSubsequenceGCDs = function(a) {
let gcd = (x, y) => !y ? x : gcd(y, x % y)
let mx = Math.max(...a)
let c = new Array(mx + 1).fill(0)
for (let v of a) c[v]++
let ans = 0
for (let i = 1; i <= mx; ++i) {
let g = 0
for (let j = 1; j * i <= mx; ++j) {
if (!c[j * i]) continue
g = gcd(g, j)
if (g === 1) break
}
ans += g === 1
}
return ans
};
法二
"use strict";
var countDifferentSubsequenceGCDs = function(a) {
let gcd = (x, y) => !y ? x : gcd(y, x % y)
let mx = Math.max(...a)
let vis = Array(mx + 1).fill(0)
let c = Array(mx + 1).fill(0)
for (let v of a) vis[v]++
for (let i = 1; i <= mx; ++i) {
let tot = 0
for (let j = 1; j * i <= mx; ++j) tot += vis[j * i]
c[i] = tot
}
let ans = 0
for (let i = 1; i <= mx; ++i) {
let fl = c[i] > 0
if (!fl) continue
for (let j = 2; j * i <= mx; ++j) {
if (c[i] <= c[j * i]) {
fl = false
break
}
}
ans += fl
}
return ans
};