P16285 [蓝桥杯 2026 省 Python A 组] 可选数 题解
P16285 [蓝桥杯 2026 省 Python A 组] 可选数
Link: https://www.luogu.com.cn/problem/P16285
题目描述
给定 N N N 个正整数 A 1 , A 2 , … , A N A_1, A_2, \dots, A_N A1,A2,…,AN 和一个目标整数 K K K。
如果一个正整数 X X X 同时是 A 1 , A 2 , … , A N A_1, A_2, \dots, A_N A1,A2,…,AN 的公倍数,则我们称 X X X 为一个可选数。
现在,你需要找到一个最小的正整数 P P P,使得对于任意一个可选数 X X X, lcm ( X , P ) \text{lcm}(X, P) lcm(X,P)( X X X 和 P P P 的最小公倍数)都能被 K K K 整除。
输入格式
第一行包含两个整数 N N N 和 K K K。
第二行包含 N N N 个整数 A 1 , A 2 , … , A N A_1, A_2, \dots, A_N A1,A2,…,AN。
输出格式
输出一个整数,表示满足条件的最小正整数 P P P。
输入输出样例 #1
输入 #1
3 12
6 4 9
输出 #1
1
输入输出样例 #2
输入 #2
2 10
4 6
输出 #2
5
说明/提示
【评测用例规模与约定】
对于 20 % 20\% 20% 的评测用例, 1 ≤ N ≤ 20 1 \leq N \leq 20 1≤N≤20, 1 ≤ K , A i ≤ 10 6 1 \leq K, A_i \leq 10^6 1≤K,Ai≤106。
对于所有的评测用例, 1 ≤ N ≤ 2 × 10 5 1 \leq N \leq 2 \times 10^5 1≤N≤2×105, 1 ≤ K , A i ≤ 10 18 1 \leq K, A_i \leq 10^{18} 1≤K,Ai≤1018。
Solution
1. 题意
给定了数组 { A N } \{A_N\} {AN},所有元素的公倍数称为可选数,记为 X X X。
对于给定的目标数字 K K K,求 P P P 使得对任意一个可选数 X X X, lcm ( X , P ) \text{lcm}(X,P) lcm(X,P) 均能被 K K K 整除。
2. 分析
2.1 如何简化题目条件
可选数 X X X 是 A 1 , A 2 , … , A N A_1, A_2, \dots, A_N A1,A2,…,AN 的公倍数,即 X X X 一定是 L = lcm ( A 1 , A 2 , … , A N ) L = \text{lcm}(A_1, A_2, \dots, A_N) L=lcm(A1,A2,…,AN) 的倍数。
题目要求
∀ X , L ∣ X ⟹ K ∣ lcm ( X , P ) \forall X ,\quad L \mid X \implies K \mid \text{lcm}(X, P) ∀X,L∣X⟹K∣lcm(X,P)
很容易看出,只要这些 A i A_i Ai 的最小公倍数 L L L 都能够满足条件,那么所有其他的公倍数必然都满足条件。
既然最苛刻的情况出现在最小的可选数 X = L X=L X=L 时。因此原条件等价于:
K ∣ lcm ( L , P ) K \mid \text{lcm}(L, P) K∣lcm(L,P)
2.2 如何分解质因数
设 v q ( n ) v_q(n) vq(n) 表示 n n n 中质数 q q q 的指数。 K ∣ lcm ( L , P ) K \mid \text{lcm}(L, P) K∣lcm(L,P) 等价于对任意质数 q q q 均有关系:
max ( v q ( L ) , v q ( P ) ) ≥ v q ( K ) \max(v_q(L), v_q(P)) \ge v_q(K) max(vq(L),vq(P))≥vq(K)
成立。
为了使 P P P 最小,我们贪心地选择最小的 v q ( P ) ≥ 0 v_q(P) \ge 0 vq(P)≥0。不难发现:
- 若 v q ( L ) ≥ v q ( K ) v_q(L) \ge v_q(K) vq(L)≥vq(K),则条件已满足,取 v q ( P ) = 0 v_q(P) = 0 vq(P)=0(不再需要 P P P 提供这种质因子)。
- 若 v q ( L ) < v q ( K ) v_q(L) < v_q(K) vq(L)<vq(K),则必须满足 v q ( P ) ≥ v q ( K ) v_q(P) \ge v_q(K) vq(P)≥vq(K),取最小值 v q ( P ) = v q ( K ) v_q(P) = v_q(K) vq(P)=vq(K)( L L L 提供的质因子不够, P P P 需要“独自”提供足量的质因子)。
总结起来,用通俗的话讲,就是为 P P P(初始为 1 1 1)补充所需的质因子,使得 P P P 和 L L L 作为“搭档”时,达到能够整除 K K K 的最低门槛。
特别注意,这里同种质因子的指数不能 L L L 和 P P P 共同提供(这是最小公倍数的特性!),这是一个坑点!
2.3 如何判断 v q ( L ) ≥ v q ( K ) v_q(L) \ge v_q(K) vq(L)≥vq(K)
2.1 一节已经提到,只用考虑最小公倍数(记为 L L L)的情况即可。 L L L 作为所有这些 A i A_i Ai 的最小公倍数,其拥有的质因子的指数必然是所有 A i A_i Ai 的最大值。所以 v q ( L ) = max i v q ( A i ) v_q(L) = \max_{i} v_q(A_i) vq(L)=maxivq(Ai)。
从而有
v q ( L ) ≥ v q ( K ) ⟺ ∃ i , v q ( A i ) ≥ v q ( K ) ⟺ ∃ i , q v q ( K ) ∣ A i v_q(L) \ge v_q(K) \iff \exists i,\ v_q(A_i) \ge v_q(K) \iff \exists i,\ q^{v_q(K)} \mid A_i vq(L)≥vq(K)⟺∃i, vq(Ai)≥vq(K)⟺∃i, qvq(K)∣Ai
也就是说,我们只需要对 K K K 的每个质因子幂 q e ∥ K q^e \parallel K qe∥K,检查是否存在某个 A i A_i Ai 能被 q e q^e qe 整除。
- 若存在,说明 L L L 已经包含了足够的 q e q^e qe, P P P 不需要乘 q e q^e qe。
- 若不存在,说明 L L L 缺少该质因子幂, P P P 必须乘上 q e q^e qe。
概括起来还是 2.2 一节里的“补全整除门槛 + 不能‘合资’”的思想。
2.4 如何判断素数并从 10 18 10^{18} 1018 的数据范围里全身而退
就本例而言,对于大数,分解质因数需要使用 Miller-Rabin 素性检测算法 [1] 以及 Pollard’s rho 算法 [2]。就本题而言,只需要测定 37 37 37 以内的质数,即可绝对确定素性。
分解出 K = ∏ q j e j K = \prod q_j^{e_j} K=∏qjej 后,对每个 q j e j q_j^{e_j} qjej 遍历数组 A A A 检查整除性(是否“拥有”足够的质因子次数)即可。
3. 代码
import sys
import random
sys.setrecursionlimit(3000)
def gcd(a, b):
while b:
a, b = b, a % b
return a
def miller_rabin(n):
if n < 2: return False
if n == 2 or n == 3: return True
if n % 2 == 0: return False
d, r = n - 1, 0
while d % 2 == 0:
d //= 2
r += 1
for a in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]:
if n <= a: break
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
def pollard_rho(n):
if n % 2 == 0: return 2
while True:
x = random.randint(2, n - 1)
y = x
c = random.randint(1, n - 1)
d = 1
while d == 1:
x = (x * x + c) % n
y = (y * y + c) % n
y = (y * y + c) % n
d = gcd(abs(x - y), n)
if d == n:
break
if d != 1 and d != n:
return d
def factorize(n):
factors = {}
for p in [2, 3, 5]:
while n % p == 0:
factors[p] = factors.get(p, 0) + 1
n //= p
d = 7
while d * d <= n and d <= 1000000:
if n % d == 0:
cnt = 0
while n % d == 0:
cnt += 1
n //= d
factors[d] = factors.get(d, 0) + cnt
d += 2
if n > 1:
stack = [n]
while stack:
curr = stack.pop()
if curr == 1: continue
if miller_rabin(curr):
factors[curr] = factors.get(curr, 0) + 1
else:
divisor = pollard_rho(curr)
stack.append(divisor)
stack.append(curr // divisor)
return factors
def solve():
input_data = sys.stdin.read().split()
if not input_data:
return
it = iter(input_data)
N = int(next(it))
K = int(next(it))
A = [int(next(it)) for _ in range(N)]
if K == 1:
print(1)
return
k_factors = factorize(K)
P = 1
for p, exp in k_factors.items():
pe = p ** exp
covered = False
for a in A:
if a % pe == 0:
covered = True
break
if not covered:
P *= pe
print(P)
if __name__ == "__main__":
solve()
更多推荐


所有评论(0)