本题目结合素数求解,按特定幂次要求,对整数进行因子分解,考察常见算法(素数求解)、降低时间复杂度的方法,难度简单。真题跳转官网查看。
真题来源: 因子化简
官网地址:www.cspro.org(模拟考试入口)
输入 q 组整数 n i n_i ni和权重 k i k_i ki,i=1,2,…,q;求每组整数的素数因子 p 1 , p 2 , . . . , p m p_1,p_2,...,p_m p1,p2,...,pm,使该整数按素数幂的积表示,即 n = p 1 t 1 × p 2 t 2 × . . . × p m t m n = p_1^{t_1} \times p_2^{t_2} \times ... \times p_m^{t_m} n=p1t1×p2t2×...×pmtm;将整数依照每组的权重 k i k_i ki,剔除幂 < k i
def is_prime(num): # 判断是否为素数
if num <= 1:
return False
if num == 2:
return True
for i in range(2, int(num ** 0.5)+1):
if num % i == 0:
return False
return True
def prime_item(num): # 求素数因子:因子对应的指数
# 求 2到sqrt(10**10)的素数因子
prime_li = []
prime_dic = {}
for i in range(2, 100000): # 因子
if is_prime(i):
prime_dic[i] = 0
prime_li.append(i)
# 求素数因子的指数
for prime in prime_li:
while num % prime == 0:
prime_dic[prime] += 1
num = num / prime
if num == 1:
break
# 处理非因子的素数
del_key = []
for key in prime_dic.keys():
if prime_dic[key] == 0:
del_key.append(key)
for key in del_key:
del prime_dic[key]
return prime_dic
if __name__ == "__main__":
q = int(input()) # 查询个数
for i in range(q):
mlc = 1 # 乘积结果
n, k = map(int, input().split()) # n:整数 k:阈值
primes = prime_item(n)
for item in primes.items():
if item[1] >= k:
mlc *= item[0]**item[1]
print(mlc)
在题解一中,prime_item(num) 函数执行了 q 次,所以求 2 → 1 0 5 2 \rightarrow 10^{5} 2→105的素数因子也执行了 q 次,实际上只需一次生成 prime_li 即可。
def is_prime(num): # 判断是否为素数
if num <= 1:
return False
if num == 2:
return True
for i in range(2, int(num ** 0.5)+1):
if num % i == 0:
return False
return True
def get_prime_li(): # 求2到sqrt(10**10)的素数因子
prime_li = []
for i in range(2, 100000): # 因子
if is_prime(i):
prime_li.append(i)
return prime_li
def get_prime_dic(num, prime_li): # 求素数因子:因子对应的指数
prime_dic = {}
for p in prime_li: # 初始化
prime_dic[p] = 0
# 求素数因子的指数
for prime in prime_li:
while num % prime == 0:
prime_dic[prime] += 1
num = num / prime
if num == 1:
break
# 处理非因子的素数
del_key = []
for key in prime_dic.keys():
if prime_dic[key] == 0:
del_key.append(key)
for key in del_key:
del prime_dic[key]
return prime_dic
if __name__ == "__main__":
q = int(input()) # 查询个数
prime_li = get_prime_li()
for i in range(q):
mlc = 1 # 乘积结果
n, k = map(int, input().split()) # n:整数 k:阈值
primes = get_prime_dic(n, prime_li)
for item in primes.items():
if item[1] >= k:
mlc *= item[0]**item[1]
print(mlc)
可将本题看作是三个问题。问题一:判断整数n是否为素数;问题二:求整数n以内的所有素数;问题三:求n的因子分解形式,素数因子的乘积。
问题一:由于因子总是程度出现的。例如: n % a ≡ 0 ⇔ n ≡ a ∗ b n \% a \equiv 0 \Leftrightarrow n \equiv a *b n%a≡0⇔n≡a∗b 则有 m i n ( a , b ) ≤ n ≤ m a x ( a , b ) min(a,b) \leq \sqrt n \leq max(a,b) min(a,b)≤n≤max(a,b) 。因此只需检查 [ 2 , n ] [\ 2,\sqrt n \ ] [ 2,n ] 内是否有 n 的因子即可,若有,则 n 不是素数返回 False ;无,则 n 是素数,返回 True 。时间复杂度为 O ( n ) O(\sqrt n) O(n)
def is_prime(num): # 判断num是否为素数
for i in range(2, int(num ** 0.5)+1):
if num % i == 0:
return False
return True
问题二:利用埃拉托斯特尼筛法,对于任意大于1的自然数**x,kx(k>1)**是合数。由于因数因数是成对存在的,所以只需要筛选出所有 1 2 n u m {1 \over 2} num 21num以内的素数。时间复杂度为 O ( 1 2 n n ) O({1 \over 2}n\sqrt n) O(21nn)
def get_primes(num): # 素数筛选算法,求num折半以内的所有素数
prime_li = [True] * (num + 1) # 初始化:设0-num所有数为素数
for i in range(2, num//2 + 1): # 对任意大于1的整数i,ji(j>1)是合数
for j in range(2, num//i + 1): # 筛除i的倍数
prime_li[j * i] = False
return prime_li
问题三:求 n 的因子分解形式。结合问题二求素因子考虑,无需求出 n 所有的素因子,将 ≤ n \leq \sqrt n ≤n的素因子除去,剩余项 = 1即可。利用元组存储**(素因子,幂)。具体执行时,利用埃拉托斯特尼筛法,在找到一个整数 n 的因子 i 后,将 i 从 n 中除去,保证每个 i 都是素因子**,除法执行的次数就是幂次。时间复杂度为 O ( n ) O(\sqrt n) O(n)。
def decompose(num): # 将整数num用因子形式表示 (因子,幂)
ans = []
i = 2
while i * i <= num: # 检查2-sqrt(n)
tmp = 0
while num % i == 0: # 素数筛选算法, 筛掉i的倍数,每筛一次,i的幂次+1
tmp += 1
num //= i
if tmp > 0:
ans.append((i, tmp))
i += 1
if num > 1: # 大于sqrt(n)的素因子最多只有1个
ans.append((num, 1))
return ans
def decompose(num): # 将整数num用因子形式表示 (因子,幂)
ans = []
i = 2
while i * i <= num: # 检查2-sqrt(n)
tmp = 0
while num % i == 0: # 素数筛选算法, 筛掉i的倍数,每筛一次,i的幂次+1
tmp += 1
num //= i
if tmp > 0:
ans.append((i, tmp))
i += 1
if num > 1: # 大于sqrt(n)的素因子最多只有1个
ans.append((num, 1))
return ans
if __name__ == "__main__":
q = int(input()) # 查询个数
for i in range(q):
mlc = 1 # 乘积结果
n, k = map(int, input().split()) # n:整数 k:阈值
num_primes = decompose(n) # 求n以内的所有素数因子,并用因子形式表示
for item in num_primes:
if item[1] >= k:
mlc *= item[0]**item[1]
print(mlc)
第32、31、30届CSP的第二题,更倾向于基础数学知识应用方面的模拟,对降低时间复杂度以及对子任务及其条件理解程度的考察。本题目若能够合理利用 1
上述题解的时间复杂度如下:
O 标准题解 = O ( n ) × [ O ( m a x ( 幂次 ) n ) + O ( 1 ) ) ≈ O ( n n ) O_{标准题解} = O(n) \times [O(max(幂次)\sqrt n) + O(1)) \approx O(n \sqrt n) O标准题解=O(n)×[O(max(幂次)n)+O(1))≈O(nn)
O 题解一优化 = O ( n ) × { O ( n ) + O [ m a x ( 幂次 ) n ] + O ( 1 ) } ≈ O ( n 2 ) O_{题解一优化} = O(n) \times \{O(\sqrt n)+O[max(幂次)n]+O(1)\} \approx O(n^2) O题解一优化=O(n)×{O(n)+O[max(幂次)n]+O(1)}≈O(n2)
O 题解一 = O ( n ) × { 999998 × O ( n ) + O [ m a x ( 幂次 ) n ] + 2 O ( n ) + O ( 1 ) } ≈ m a x { 999998 × O ( n n ) , [ m a x ( 幂次 ) + 2 ] × O ( n 2 ) } O_{题解一} = O(n) \times \{999998\times O(\sqrt n)+O[max(幂次)n] + 2O(n) + O(1)\} \approx max\{999998 \times O(n \sqrt n),[max(幂次)+2] \times O(n^2)\} O题解一=O(n)×{999998×O(n)+O[max(幂次)n]+2O(n)+O(1)}≈max{999998×O(nn),[max(幂次)+2]×O(n2)}