将整数分解质因数,根据算术基本定理转化为质因子多重集的全排列计数问题。
OJ: roj
题目 ID: 1628
难度:普及+/提高-
标签:数论质因数分解组合数学
创建: 2026-09-30 22:54
更新: 2026-10-07 13:50
形式化题目
给定正整数
且 ; - 对于所有
,均有 。
求该序列的最大可能长度
正解
思路
1. 最大长度与质因数分解
设
由于要求每个
因此,根据算术基本定理,将
2. 最大长度序列的个数
整除链的每一步相当于选择一个质因子乘上去。
因此,构造一条最大长度的链,等价于将这
这构成了质因子多重集的全排列计数问题。方案总数为:
3. 算法实现
由于
代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-07-05 21:47
# update_at: 2026-07-05 21:47
import sys
import math
from functools import reduce
from operator import mul
# 预处理素数表(由于 x <= 2^20 ≈ 1048576,质因子分解只需要试除到 sqrt(x) <= 1024)
MAX_P = 1024
IS_PRIME = [True] * (MAX_P + 1)
IS_PRIME[0] = IS_PRIME[1] = False
for i in range(2, int(MAX_P**0.5) + 1):
if IS_PRIME[i]:
IS_PRIME[i * i : MAX_P + 1 : i] = [False] * len(range(i * i, MAX_P + 1, i))
PRIMES = [i for i, p in enumerate(IS_PRIME) if p]
def factorize(x: int) -> list[int]:
"""分解正整数 x 的质因数,返回各质因子的出现次数列表。"""
counts = []
for p in PRIMES:
if p * p > x:
break
if x % p == 0:
cnt = 0
while x % p == 0:
cnt += 1
x //= p
counts.append(cnt)
if x > 1:
counts.append(1)
return counts
def solve() -> None:
lines = sys.stdin.read().split()
if not lines:
return
for line in lines:
x = int(line)
counts = factorize(x)
max_len = sum(counts)
# 多重集的排列数:(sum(counts))! / (count_1! * count_2! * ... * count_k!)
ans = math.factorial(max_len) // reduce(mul, (math.factorial(c) for c in counts), 1)
print(f"{max_len} {ans}")
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 23:14
* update_at: 2026-10-05 23:14
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXP = 1024; // x <= 2^20,试除到 sqrt(x) <= 1024 即可
int primes[200]; // 预处理得到的 <= 1024 的素数
int prime_cnt; // 素数个数
ll fact[64]; // fact[i] = i!,x <= 2^20 时质因子总个数 k <= 20
// 埃氏筛预处理 <= MAXP 的素数,供试除分解质因数使用。
void init_primes() {
bool is_prime[MAXP + 1];
for (int i = 0; i <= MAXP; i++) is_prime[i] = true;
is_prime[0] = false;
is_prime[1] = false;
for (int i = 2; i * i <= MAXP; i++) {
if (!is_prime[i]) continue;
for (int j = i * i; j <= MAXP; j += i) is_prime[j] = false;
}
prime_cnt = 0;
for (int i = 2; i <= MAXP; i++) {
if (is_prime[i]) primes[prime_cnt++] = i;
}
}
// 预处理阶乘,用于计算多重集排列数。
void init_fact() {
fact[0] = 1;
for (int i = 1; i < 64; i++) fact[i] = fact[i - 1] * i;
}
// 对单个 x 求答案:最大链长 = 质因子指数之和 k,方案数 = k! / (各指数阶乘之积)。
void solve_one(ll x) {
ll total = 0; // 质因子指数之和,即最大链长 k
ll denom = 1; // 分母:各质因子指数阶乘的乘积
ll rest = x;
for (int i = 0; i < prime_cnt; i++) {
ll p = primes[i];
if (p * p > rest) break;
if (rest % p == 0) {
ll cnt = 0;
while (rest % p == 0) {
rest /= p;
cnt++;
}
total += cnt;
denom *= fact[cnt];
}
}
if (rest > 1) total += 1; // 剩下一个大于 1 的质因子
cout << total << " " << fact[total] / denom << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init_primes();
init_fact();
ll x;
while (cin >> x) { // 多组数据读到文件结束
solve_one(x);
}
return 0;
}复杂度
- 时间复杂度:
- 预处理:筛选
的素数,耗时可忽略不计。 - 单次查询:试除最多 172 个素数,时间复杂度为
;阶乘及除法计算仅需 (其中 )。 - 总时间复杂度为
,在时限内非常充裕。
- 预处理:筛选
- 空间复杂度:只需存储
以内的素数表,空间复杂度为 。
总结
本题的关键在于通过商的拆分建立起整除链与算术基本定理的联系:
- 链长极大化
链的每一步乘数不可约(均为质数),链长即为质因子重数之和; - 序列计数
质因子多重集的全排列。
图示解析
以样例中
- 最大长度
; - 方案数为
种。
下表展示了质因子的 6 种不同排列与生成的因子链的对应关系:
| 排列 |
对应的整除链 |
|---|---|