数论——求解约数个数的各种方法

求解约数个数的各种方法

已知序列,求序列中每个数与其它数的约数关系

轻拍牛头

今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏.

贝茜让 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   ˜k1个质因子已经在 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

思路

最大反素数:约数个数最多的最小的数
存在如下性质

  1. 质因子次数是单调非递增的
  2. 质因子最多枚举到13
  3. 质因子次数最多为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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值