题解:学而思编程 计算因质数
【题目来源】
学而思编程:计算因质数
【题目描述】
小猴非常喜欢研究数学问题,这天他学习了质因数的定义,觉得很有意思,他想每个大于 \(1\) 的数 \(x\) 都会有若干的质因数,如果把 \(x\) 的质因数乘起来会有什么规律和特殊的性质么?他把这个乘积定义为“因质数”,决定深入研究一下,那第一步就是先把每个数的“因质数”求出来。
现在给出 \(N\) 个正整数,求出每个数的因质数是多少。
【输入】
第一行一个正整数 \(N\),表示数的个数;
第二行 \(N\) 个正整数 \(X_1,X_2,\dots,X_N\)。
【输出】
一行用空格隔开的 \(N\) 个正整数,分别表示 $ X_1,X_2,\dots,X_N$ [的因质数。
【输入样例】
2
8 13
【输出样例】
2 13
【核心思想】
-
问题分析:给定 \(N\) 个正整数,求每个数的"因质数"——即该数所有不同质因数的乘积。例如 \(8 = 2^3\),质因数为 \(\{2\}\),因质数为 \(2\);\(13\) 是质数,因质数为 \(13\)。这是一个筛法预处理 + 质因数分解问题,关键在于利用类似埃氏筛的方法,预处理每个数的最小质因数并累乘不同质因数。
-
算法选择:
- 线性筛思想:遍历每个质数 \(i\),将 \(i\) 的倍数中尚未被赋值或需要乘上 \(i\) 的位置更新
- 预处理标记:
f[x]存储 \(x\) 的所有不同质因数的乘积
-
关键步骤:
- 预处理函数
init():- 遍历 \(i\) 从 \(2\) 到 \(10^6\):
- 若
f[i] == 0(\(i\) 是质数):- 遍历 \(j\) 从 \(i\) 到 \(10^6\),步长 \(i\)(\(i\) 的所有倍数):
- 若
f[j] == 0:f[j] = i(首次遇到质因数,赋值) - 否则:
f[j] *= i(累乘新的质因数)
- 若
- 遍历 \(j\) 从 \(i\) 到 \(10^6\),步长 \(i\)(\(i\) 的所有倍数):
- 若
- 遍历 \(i\) 从 \(2\) 到 \(10^6\):
- 主函数:
- 调用
init()预处理 - 读取 \(N\)
- 遍历每个数 \(x\):输出
f[x]
- 调用
- 预处理函数
-
时间/空间复杂度:
- 时间复杂度:\(O(V \log \log V)\),\(V = 10^6\),类似埃氏筛的复杂度
- 空间复杂度:\(O(V)\),预处理数组
f[1..V]
-
筛法预处理的核心思想:
- 质因数乘积的累积性:每个合数的因质数等于其所有不同质因数的乘积。当用质数 \(i\) 筛其倍数时,若该倍数首次被筛则赋值 \(i\),否则乘上 \(i\),确保每个质因数只贡献一次
- 最小质因数优先:由于从小到大遍历,每个数第一次被赋值的是其最小质因数,后续更大的质因数通过
*=累乘 - 离线预处理:值域固定为 \(10^6\),与查询无关,预先计算所有数的因质数,查询时 \(O(1)\) 输出
- 重复质因数处理:
f[j] == 0的判断确保同一质因数的幂次只乘一次,实现"不同质因数"的要求 - 适用于"多查询 + 固定值域质因数统计"类问题,筛法预处理将每次查询优化到常数时间
【算法标签】
质数
【代码详解】
#include <bits/stdc++.h>
using namespace std;
// 预处理
int n, x, f[1000005];
void init()
{
for (int i=2; i<=1000000; i++) { // 计算质因数模板
if (f[i]==0) { // 如果f[i]没有数字
for (int j=i; j<=1000000; j+=i) { // 从2遍历到1000000,按i的倍数步进
if (f[j]==0) f[j] = i; // 将每个f[j]初始化为i,如2,3,5...。
else f[j] *= i; // 然后每次计算,乘上i这个质数(因为有了f[i]==0判断,即乘以过i的不再乘i)
}
}
}
}
int main()
{
init(); // 预处理每个数的质因数乘积
cin >> n;
while (n--) {
scanf("%d", &x);
printf("%d ", f[x]);
}
return 0;
}
【运行结果】
2
8 13
2 13
浙公网安备 33010602011771号