把方程变形为 (x−n!)(y−n!)=(n!)²,答案即 (n!)² 的约数个数:线性筛最小质因子分解 1..n 累加指数后连乘 ∏(2e+1)。
OJ: roj
题目 ID: 1624
难度:普及+/提高-
标签:
创建: 2026-09-30 22:43
更新: 2026-10-07 13:50
形式化题目
给定正整数
的正整数对
样例:
这道题的关键在于:
正解
思路
第一步:把方程变形成乘积式
设
把左边整体移到右边,再"凑"一个
这一步把加法方程变成了乘积方程,是数方程计数的经典手法(和
第二步:解的个数 = 的约数个数
若
反过来,取
则
所以"正整数解
用样例
| 约数 |
解 | ||
|---|---|---|---|
第三步:计算 ——只需要 的质因数分解
若
所以整个问题坍缩成一件事:求出
第四步:线性筛 + 逐数分解
- Legendre 公式:
; - 逐数分解:把
每个数分解,指数累加。
两者复杂度同阶。这里选逐数分解,配合线性筛的最小质因子:筛出每个合数的最小质因子 exp[p],就得到了
这里有一个容易踩的坑:必须先把所有
代码
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-09-30 22:43
# update_at: 2026-09-30 22:43
from math import prod
from sys import stdin
MOD = 10**9 + 7
def solve() -> None:
n = int(stdin.buffer.read())
# 线性筛:只记录每个合数的最小质因子,其余因数分解全靠它跳转。
min_prime = [0] * (n + 1)
primes: list[int] = []
for v in range(2, n + 1):
if min_prime[v] == 0: # v 没有更小的质因子 → v 是质数
min_prime[v] = v
primes.append(v)
for p in primes:
if p > min_prime[v] or p * v > n:
break
min_prime[p * v] = p
# 先把 2..n 每个数的质因子指数累加起来,得到 n! 的标准分解。
exp = [0] * (n + 1)
for v in range(2, n + 1):
while v > 1:
p = min_prime[v]
while v % p == 0: # 除尽同一个质因子
v //= p
exp[p] += 1
# ans = d((n!)^2):n! 里指数为 E 的质因子,平方后指数 2E,贡献 2E+1。
print(prod(2 * e + 1 for e in exp) % MOD)
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:10
* update_at: 2026-10-05 23:12
*/
// main.cpp:正解。把 1/x + 1/y = 1/n! 变形为 (x-n!)(y-n!)=(n!)^2,
// 正整数解数目 = (n!)^2 的约数个数,用线性筛分解 1..n 求质因子指数后连乘。
#include <iostream>
typedef long long ll;
const int MAXN = 1000000 + 5; // n 的上界
const ll MOD = 1000000007;
int min_prime[MAXN]; // min_prime[v] = v 的最小质因子,线性筛中顺带求出
int primes[MAXN]; // primes[1..prime_cnt] 存放筛出的质数
int prime_cnt;
ll exp_of_prime[MAXN]; // exp_of_prime[p] = p 在 n! 中的指数
int n;
// 线性筛:每个合数只被它的最小质因子筛掉一次,同时记录最小质因子。
void linear_sieve() {
for (int v = 2; v <= n; v++) {
if (min_prime[v] == 0) { // 没有更小的质因子,说明 v 是质数
min_prime[v] = v;
prime_cnt++;
primes[prime_cnt] = v;
}
for (int i = 1; i <= prime_cnt; i++) {
int p = primes[i];
if (p > min_prime[v] || p > n / v) { // p * v > n 时停止
break;
}
min_prime[p * v] = p;
}
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n;
linear_sieve();
// 把 2..n 每个数分解质因数,指数累加得到 n! 的标准分解。
for (int i = 2; i <= n; i++) {
int x = i; // 用临时变量分解,不能改动外层循环变量 i
while (x > 1) {
int p = min_prime[x];
while (x % p == 0) {
x /= p;
exp_of_prime[p]++;
}
}
}
// 答案 = d((n!)^2) = ∏ (2 * e_p + 1),其中 e_p 是 p 在 n! 中的指数。
ll answer = 1;
for (int i = 1; i <= prime_cnt; i++) {
int p = primes[i];
answer = answer * (2 * exp_of_prime[p] + 1) % MOD;
}
std::cout << answer << "\n";
return 0;
}复杂度
- 时间:线性筛
,逐数分解总代价为 ( 是质因子个数),为 级;合计 , 时 C++ 轻松通过,Python 实现也在数秒内完成。 - 空间:两个长度
的整数数组, 。 - 取模注意:指数
本身不超过 量级(约 ,即 很小),不需要对指数取模,只对乘积取模。
总结
- 核心变形:
,其中 。 - 由
知解与 的有序因子对一一对应,答案 。 - 实现上只需线性筛最小质因子,把
逐数分解累加指数,最后连乘 取模。 - 这道题的通用套路:“倒数和 = 1” 型方程计数 → 移项凑平方 → 约数个数 → 质因数分解(配合 Legendre / 线性筛)。