求解约数个数的各种方法
已知序列,求序列中每个数与其它数的约数关系
轻拍牛头
今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏.
贝茜让 N 头奶牛(编号 1 到 N)坐成一个圈。除了 1号与 N号奶牛外,i 号奶牛与 i−1 号和 i+1 号奶牛相邻,N 号奶牛与 1 号奶牛相邻。
农夫约翰用很多纸条装满了一个桶,每一张纸条中包含一个 1 到 1000000 之间的数字。接着每一头奶牛 i 从桶中取出一张纸条,纸条上的数字用 Ai
表示。
所有奶牛都选取完毕后,每头奶牛轮流走上一圈,当走到一头奶牛身旁时,如果自己手中的数字能够被该奶牛手中的数字整除,则拍打该牛的头。
牛们希望你帮助他们确定,每一头奶牛需要拍打的牛的数量。
即共有 N 个整数 A1,A2,…,AN,对于每一个数 Ai,求其他的数中有多少个是它的约数。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含一个整数 Ai。
输出格式
共 N 行,第 i 行的数字为第 i 头牛需要拍打的牛的数量。
数据范围
1≤N≤105,1≤Ai≤106
输入样例:
5
2
1
2
3
4
输出样例:
2
0
2
1
3
思路
这题本质上是求对于一个给定序列,求序列中每个数,包含多少其它数是其约数。
一个正面的想法是,枚举每个数,再枚举其它数,判断约数关系,此时时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)很不乐观。
存在这样一个等价关系A1是A2的约数,那么A2是A1的倍数。对于每个数,我们可以通过枚举它的倍数来记录它倍数的约数。
代码
N = 1000010
a = [0] * N
cnt = [0] * N
s = [0] * N
n = int(input())
for i in range(1, n + 1) :
a[i] = int(input())
cnt[a[i]] += 1
for i in set(a[1 : n + 1]) :
j = i
while j < N :
s[j] += cnt[i]
j += i
for i in range(1, n + 1) :
print(s[a[i]] - 1) # 除去自己
阶乘约数
樱花
给定一个整数 n,求有多少正整数数对 (x,y)
满足 1/x+1/y=1/n!。
输入格式
一个整数 n。
输出格式
一个整数,表示满足条件的数对数量。
答案对 109+7取模。
数据范围
1≤n≤106
输入样例:
2
输出样例:
3
样例解释
共有三个数对 (x,y)
满足条件,分别是 (3,6),(4,4),(6,3)。
思路
阶乘是一个很大的数,如果对1~n的数挨个求质因子记录个数,那将很耗时。
N
!
N!
N!中质因子p的个数就等于
1
˜
N
1~~\~~N
1 ˜N每个数包含质因子p的个数之和。在
1
˜
N
1~~\~~N
1 ˜N中,
p
k
p^k
pk的倍数,即至少包含1个质因子
p
k
p^k
pk的显然有
N
/
/
p
k
N // p^k
N//pk个。由于
1
˜
k
−
1
1~~\~~k - 1
1 ˜k−1个质因子已经在
N
/
/
p
1
k
N // p^{1 ~k}
N//p1 k中统计过,所以只需要再统计第k个质因子,即累加上
N
/
/
p
k
N // p^k
N//pk
本题1/x + 1/y = 1/n!
=> y = x * n!/(x - n!) => y = n! + (n!)^2/(x - n!)
即找到一个x使得(n!)^2/(x - n!)为正整数即可,x > n!。
此时所有满足情况的x个数即是(n!)^2的约数个数
对于约数个数我们可以用约数个数定理
代码
MOD = int(1e9) + 7
N = 1000010
st = [False] * N
primes = []
def init(n) :
for i in range(2, n + 1) :
if not st[i] :
primes.append(i)
for j in primes :
if i * j > n : break
st[i * j] = True
if i % j == 0:
break
init(N - 1)
n = int(input())
res = 1
for i in primes :
s = 0
j = n
while j :
s += j // i
j //= i
res = res * (1 + 2 * s) % MOD
print(res)
最小的约数最多的数
反素数
对于任何正整数 x,其约数的个数记作 g(x),例如 g(1)=1、g(6)=4。
如果某个正整数 x 满足:对于任意的小于 x 的正整数 i,都有 g(x)>g(i),则称 x 为反素数。
例如,整数 1,2,4,6 等都是反素数。
现在给定一个数 N,请求出不超过 N 的最大的反素数。
输入格式
一个正整数 N。
输出格式
一个整数,表示不超过 N
的最大反素数。
数据范围
1≤N≤2∗109
输入样例:
1000
输出样例:
840
思路
最大反素数:约数个数最多的最小的数
存在如下性质
- 质因子次数是单调非递增的
- 质因子最多枚举到13
- 质因子次数最多为30
搜索顺序:搜索由小到大质因子,枚举可取次数,知道质因子搜索到最后,比较约数个数,保存答案。
代码
primes = [2, 3, 5, 7, 11, 13, 17, 19, 13]
ans = 0
maxx = 0
def dfs(u, t, s, p) :
global ans, maxx
# 可行性剪枝
if p > n : return
if s > maxx or (s == maxx and p < ans) :
maxx = s
ans = p
if u == 9 : return
for i in range(1, t + 1) :
dfs(u + 1, i, s * (1 + i), p * primes[u] ** i)
n = int(input())
dfs(0, 30, 1, 1)
print(ans)
快速求约数个数
Hankson的趣味题
Hanks 博士是 BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫 Hankson。
现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数 c1和 c2 的最大公约数和最小公倍数。
现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:
已知正整数 a0,a1,b0,b1,设某未知正整数 x 满足:x 和 a0 的最大公约数是 a1;x和 b0 的最小公倍数是 b1。Hankson 的“逆问题”就是求出满足条件的正整数 x。
但稍加思索之后,他发现这样的 x 并不唯一,甚至可能不存在。
因此他转而开始考虑如何求解满足条件的 x
的个数。
请你帮助他编程求解这个问题。
输入格式
输入第一行为一个正整数 n,表示有 n 组输入数据。
接下来的 n 行每行一组输入数据,为四个正整数 a0,a1,b0,b1,每两个整数之间用一个空格隔开。
输入数据保证 a0 能被 a1 整除,b1 能被 b0 整除。
输出格式
输出共 n 行。
每组输入数据的输出结果占一行,为一个整数。
对于每组数据:若不存在这样的 x,请输出 0;
若存在这样的 x,请输出满足条件的 x 的个数;
数据范围
1≤n≤2000,1≤a0,a1,b0,b1≤2∗109
输入样例:
2
41 1 96 288
95 1 37 1776
输出样例:
6
2
思路
先枚举
b
1
b1
b1的约数,再判断是否满足以上关系。
可以通过筛好的质数,快速求出其质因子情况,最后通过组合求出其所有约数,这比试除法快。
代码
'''
gcd(x, a) = b
lcm(x, c) = d
求满足此方程组的解的个数
枚举d的约数,判别
通过分解质因数来枚举约数
'''
from math import gcd
N = 50010
st = [False] * N
primes = []
def init(n) :
for i in range(2, n + 1) :
if not st[i] :
primes.append(i)
for j in primes :
if i * j > n : break
st[i * j] = True
if i % j == 0 :
break
def dfs(u, p) :
if u == len(factors) :
dividers.append(p)
return
for i in range(factors[u][1] + 1) :
dfs(u + 1, p * factors[u][0] ** i)
def lcm(a, b) :
return a * b // gcd(a, b)
init(N - 1)
T = int(input())
factors = []
dividers = []
for _ in range(T) :
a, b, c, d = map(int, input().split())
# 分解质因数
factors = []
dividers = []
tmp = d
i = 0
while primes[i] <= tmp // primes[i] :
s = 0
while tmp % primes[i] == 0 :
s += 1
tmp //= primes[i]
if s :
factors.append([primes[i], s])
i += 1
if tmp > 1 :
factors.append([tmp, 1])
dfs(0, 1)
res = 0
for i in dividers :
if gcd(i, a) == b and lcm(i, c) == d :
res += 1
print(res)