📅  最后修改于: 2023-12-03 15:26:53.460000             🧑  作者: Mango
给定一个数组 arr
,每个元素代表一个数值范围的上限。计算每个数值范围内非互质数对的数量。两个数是互质的,当且仅当它们的最大公约数为 1。
输入: arr = [5, 10, 7]
输出: [4, 22, 12]
解释:
我们可以从最简单的方法开始思考,一次遍历数组中每个元素的所有数值,对于其中每一对数值判断它们是否互质。这种暴力解法的时间复杂度为 $O(n^2 \times \log_2 n)$。 (其中 $n$ 表示数组长度,$\log_2 n$ 表示计算两个数的最大公约数时需要进行的二分查找)
这样的复杂度非常高,在实际应用中很难承受。因此我们需要寻找更高效的解法。
首先我们可以想到如下结论:对于每个数,保证其左侧已有的所有数与其互质,那么在其展开所有数值时,与之互质的数的数量将与所有数值的总数相同。
事实上,我们可以通过递归实现这种思路。将一个数值范围 [1,n] 分别拆分为:
这样,在计算某个数值范围内的非互质数对时,我们可以先计算该区间内小于等于 p^k 的非互质数对数量,然后将剩下的范围的求解交给递归函数。
最后一个问题就是计算区间 [1,p^k] 内的非互质数对数量。对于每个质数 p,它们之间的数的最大公约数一定是 p,因此数量为 $\lfloor p^{k-1} \rfloor \times (p-1)$。
综上,我们可以得到如下的代码实现:
def count_non_coprime_pairs(n: int) -> int:
if n == 1:
return 0
res = 0
p = 2
# 找到 n 范围内的最小质因数
while p * p <= n and n % p != 0:
p += 1
# 如果 n 本身是质数,则对非互质数对数量的贡献值为 1
if p * p > n:
return n - 1
# 计算小于等于 p^k 的非互质数对数量
k = 0
while n % p == 0:
k += 1
n //= p
res += p ** (k - 1) * (p - 1)
# 递归计算剩下区间内的非互质数对数量
res += (n - 1) * count_non_coprime_pairs(p ** k)
return res
def count_pairs(arr: List[int]) -> List[int]:
res = []
for n in arr:
res.append(n * (n - 1) - count_non_coprime_pairs(n))
return res
其中 count_non_coprime_pairs
函数用于计算区间 [1,n] 内的非互质数对数量,而 count_pairs
函数是问题的实际求解函数。