算术基本定理例题—— X的因子链、聪明的燕姿

1295. X的因子链

输入正整数 X,求 X 的大于 1 的因子组成的满足任意前一项都能整除后一项的严格递增序列的最大长度,以及满足最大长度的序列的个数。

输入格式
输入包含多组数据,每组数据占一行,包含一个正整数表示 X。

输出格式
对于每组数据,输出序列的最大长度以及满足最大长度的序列的个数。

每个结果占一行。

数据范围
1≤X≤220
输入样例:
2
3
4
10
100
输出样例:
1 1
1 1
2 1
2 2
4 6

题解
由算术基本定理得:
所有正整数都可以分解成质因子乘积的形式,

N = P [ 1 ] d [ 1 ] ∗ P [ 2 ] d [ 2 ] … P [ k ] d [ k ] N = P[1]^{d[1]} * P[2]^{d[2]} … P[k]^{d[k]} N=P[1]d[1]P[2]d[2]P[k]d[k]
然后,我们就可以使用上述组合,第一个数是一个质因子,第二个数是两个质因子,…
比如: 20 = 2 2 ∗ 5 1 20 = 2^2 * 5^1 20=2251
则将两个2和一个5:
2,22,225
2,2
5,252
5, 52,52*2
(1+2)!/(1!+2!)

N = (1 << 20) + 100
primes = []
st = [False for i in range(N)]
mprimes = [0 for i in range(N)]

def get_primes(n):
    for i in range(2, n+1):
        if st[i] == False:
            primes.append(i)
            mprimes[i] = i
        j = 0
        while i * primes[j] <= n:
            st[i * primes[j]] = True
            mprimes[i * primes[j]] = primes[j]
            if i % primes[j] == 0:
                break
            j += 1
get_primes(N - 1)
while True:
    try:
        n = int(input())
        res = []
        while n != 1:
            
            j = mprimes[n]
            # print(n, j)
            cnt = 0
            while n % j == 0:
                cnt += 1
                n //= j
            res.append(cnt)
            if n == 1:
                break
        if len(res) == 0:
            print(0,0)
        else:
            ans = 1
            j = 0
            for it in res:
                for i in range(1, it + 1):
                    j += 1
                    ans *= j
                    ans //= i
        print(j, ans)
    except:
        break

1296. 聪明的燕姿

城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁。

可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!

燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字 S,那么自己等的人手上的号码牌数字的所有正约数之和必定等于 S。

所以燕姿总是拿着号码牌在地铁和人海找数字(喂!这样真的靠谱吗)。

可是她忙着唱《绿光》,想拜托你写一个程序能够快速地找到所有自己等的人。

输入格式
输入包含 k 组数据。

对于每组数据,输入包含一个号码牌 S。

输出格式
对于每组数据,输出有两行。

第一行包含一个整数 m,表示有 m 个等的人。

第二行包含相应的 m 个数,表示所有等的人的号码牌。

注意:你输出的号码牌必须按照升序排列。

数据范围
1≤k≤100,
1≤S≤2×109
输入样例:
42
输出样例:
3
20 26 41

题解
算术基本定理
所有正整数都可以分解成质因子乘积的形式:
N = P 1 a 1 ∗ P 2 a 2 ∗ … ∗ P n a n N = P_1^{a_1} * P_2^{a_2} * … * P_n^{a_n} N=P1a1P2a2Pnan

则N的约数个数: ( a 1 + 1 ) ( a 2 + 1 ) … ( a n + 1 ) (a_1+1)(a_2+1)…(a_n+1) (a1+1)(a2+1)(an+1)
约数之和
S = ( 1 + p 1 + p 1 2 + … + p 1 a 1 ) ( 1 + p 2 + p 2 2 + … + p 2 a 2 ) … ( 1 + p n + p n 2 + … + p n a n ) S = (1+p_1+p_1^2+…+p_1^{a_1})(1+p_2+p_2^2+…+p_2^{a_2})…(1+p_n+p_n^2+…+p_n^{a_n}) S=(1+p1+p12++p1a1)(1+p2+p22++p2a2)(1+pn+pn2++pnan)
原因
观察约束和公式,假设这个时候有一个约数和S满足 ( 1 + 2 ) ( 1 + 2 + 2 2 ) ( 1 + 2 + 2 2 + … + 2 k ) (1+2)(1+2+2^2)(1+2+2^2+…+2^k) (1+2)(1+2+22)(1+2+22++2k) =3x5x9x17x33x65x129 = 635 037 975 就已经接近1e9的量级了,可见符合条件的项不会很多。
dfs进行搜索,得到符合条件的 ( 1 + p n + p n 2 + … + p n a n ) (1+p_n+p_n^2+…+p_n^{a_n}) (1+pn+pn2++pnan)能够使得
S能够整除,即 s   %   ( 1 + p n + p n 2 + … + p n a n ) = = 0 s\ \%\ (1+p_n+p_n^2+…+p_n^{a_n})== 0 s % (1+pn+pn2++pnan)==0
然后 s   / = ( 1 + p n + p n 2 + … + p n a n ) s \ / =(1+p_n+p_n^2+…+p_n^{a_n}) s /=(1+pn+pn2++pnan),再dfs到下一层。
即:
应该先从小到大枚举P

for(p : 2,3,5,7,...)
    for(a : 1,2,3,...)
        if(S mod (1+p1+p1^2+...+p1^a1) == 0)
            dfs(下一层)

特殊情况:
如果ai = 1的话,S = (1+Pi)的时候,因为Pi为质数,那么S-1也一定为质数,那么这个时候只需要判断S-1是否为质数即可

即S只会有两种情况

  1. 一种情况包括一个因子里面有(1+Pi)
    S = (1+Pi)(1+Pj+Pj^2…)
  2. 一种情况不包括(1+Pi)
    S = (1+Pi+Pi^2)(1+…)

但是这两种情况都可以看出来Pi ^2 <= S
所以我们dfs枚举Pi的上限就是 S \sqrt S S
dfs参数设置:

  1. last参数:表示上一个枚举的质数是谁(下标)
  2. product参数:表示质因子的乘积和,即最后的答案。
  3. S参数:代表着从一开始的S除以 ( 1 + p k + p k 2 + … + p k a k ) (1+p_k+p_k^2+…+p_k^{a_k}) (1+pk+pk2++pkak)后剩余的乘积

S-1一定要大于上一层的质数。
因为我们要保证质数是从小到大枚举的,只有剩下的S-1是大于上一层的质数的时候,(1+S)才有可能成为最初的那个S的一个因子

因为我们要表示 S ’ = ( 1 + 2 + 2 2 + … ) ( 1 + 3 + 3 2 + … ) … ( 1 + S ) S’ = (1+ 2 + 2^2+…)(1 + 3 + 3^2 + …)…(1+S) S=(1+2+22+)(1+3+32+)(1+S)
越往后枚举对应的那个Pi也越大,所以S-1一定要大于上一层的质数才能满足条件.

N =1000010
primes = []
st = [False for i in range(N)]

def get_primes(n):
    for i in range(2, n+1):
        if st[i] == False:
            primes.append(i)
        j = 0
        while i * primes[j] <= n:
            st[i * primes[j]] = True
            if i % primes[j] == 0:
                break
            j += 1
get_primes(N - 1)
res = []

def is_prime(n):
    if n < N:
        if st[n] == True:
            return False
        else:
            return True
    i = 0
    while primes[i] <= n // primes[i]:
        if n % primes[i] == 0:
            return False
        i += 1
    return True
    
def dfs(last, product, S):
   
    if S == 1:
        res.append(product)
        return 
    
    if last == -1:
        temp = 1
    else:
        temp = primes[last]
        
    if S - 1 > temp and is_prime(S - 1):
        res.append(product * (S - 1))
   
    i = last + 1
    
    while primes[i] <= S//primes[i]:
        t = 1
        j = primes[i]
        while t <= S:
            
            t += j
            if S % t == 0:
                dfs(i, product * j, S // t)
            j *= primes[i]
        i += 1
        
while True:
    try:
        res = []
        n = int(input())
        dfs(-1, 1, n)
        print(len(res))
        res.sort()
        for it in res:
            print(it, end = " ")
        if len(res) != 0:
            print()
    except:
        break


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值