题目描述
定义 F(i)F(i) 表示整数 ii 的最小质因子。现给定一个正整数 NN,请你求出 \sum^{n}_{2}F(i)∑2nF(i)。
输入描述
第 11 行为一个整数 TT,表示测试数据数量。
接下来的 TT 行每行包含一个正整数 NN。
1 \leq T \leq 10^61≤T≤106,2\leq N \leq 3\times 10^62≤N≤3×106。
输出描述
输出共 TT 行,每行包含一个整数,表示答案。
输入输出样例
示例 1
输入
3
5
10
15
输出
12
28
59
运行限制
语言 | 最大运行时间 | 最大运行内存 |
C++ | 1s | 256M |
C | 1s | 256M |
Java | 2s | 256M |
Python3 | 20s | 512M |
源码:
方法一:埃式筛法
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Scanner;
public class 最小质因数之和 {
/*
* 一、求2-n每个数的最小质因子
* 朴素的做法:
* for i: 2-n
* 对i求最小质因子(for j 1:i)
* O(n*n)3e6 操作次数 9e12 明显不行
* 关于质数:1.唯一分解定理(求n的质因子) 每次只能求一个数的质因子
* 2.埃氏筛 筛选从2-n的质数的个数 不是质数的数m是怎么筛掉的呢?通过m的质因子筛掉的,m第一次被谁筛掉的,是被m的最小的质因子筛掉的
* O(n*根号n) 筛2-n ----->求出2-n每一个数对应的最小质因子 1e6*1e3=1e9可以的
* 二、测试数据量t
* O(t*n*根号n) 1e15
* 预处理
* 先预处理出2-n每一个对应的解 前缀和sum[i] 表示2-i的最小质因子的和
* while(t--) O(t)
* sum[i] O(1)
*
*/
public static void main(String[] args) throws IOException {
//进行预处理
f();//求2-n中每一个数对应的最小质因子
sum();//求前缀和数组
StreamTokenizer sc=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
//int n = (int)sc.nval;
//Scanner scanner = new Scanner(System.in);
//int t = scanner.nextInt();
sc.nextToken();
int t = (int)sc.nval;
while(t-- > 0) {
sc.nextToken();
int n = (int)sc.nval;
//int n = scanner.nextInt();
System.out.println(res[n]);
//System.out.println(res1[n]);
}
}
static long res[] = new long[4000000];
private static void sum() {
// TODO Auto-generated method stub
//一次求出i :2-n
// 2-i 的最小质因子之和 前缀和数组可以在O(n)
//1 2 3 4
//1
//1+2=3
//3+3=6
for (int i = 2; i < res.length; i++) {
res[i] = res[i-1] +pre[i];
//System.out.println(pre1[i]);
}
//System.out.println(res[5]);
}
static int pre[] = new int[4000000];
static int book[] = new int[4000000];
/*
* 埃氏筛的思想
* 求1-n的素因子
* 从小到大遍历 用book[i]数组记录i是否为素数,0表示为素数,若i是素数,把所有的i的倍数的数都筛掉,也就是book[i*j]记录为1
*
* 如何在这个过程中求某个数的最小素因子------在埃氏筛中,一个数可能会被筛掉多次,但是它第一次被x筛掉,那么x就是它的最小素因子
* 比如6 当遍历到2的时候他会被2筛掉一次,当遍历到3的时候,还是会被3筛掉一次,但是我们只记录第一次的,
*/
private static void f() {
// TODO Auto-generated method stub
book[1] = 1;
book[0] = 1;
for (int i = 2; i < book.length; i++) {
//pre1[i] = i;//!!!!!!!!!!!
if(book[i] == 0) {
pre[i] = i;//!!!!!!!!!!!把i的最小素因子置为i
for (int j = i+i; j < book.length && j > 0; j += i) {
if(book[j] == 0) {//若此时j没有被标记为合数,那么j会被i消去,i是第一个消去j的因子,
//那么i也就是j对应的最小的那个素因子
pre[j] = i;//记录j最小的素因子是i
//System.out.println(j + " " + i);
}
book[j] = 1;//把j标记为素数
}
}
}
}
}
方法二:欧拉筛法
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Scanner;
public class 最小质因数之和1 {
public static void main(String[] args) throws IOException {
g();
sum1();
StreamTokenizer sc=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
//int n = (int)sc.nval;
Scanner scanner = new Scanner(System.in);
//int t = scanner.nextInt();
sc.nextToken();
int t = (int)sc.nval;
while(t-- > 0) {
sc.nextToken();
int n = (int)sc.nval;
//int n = scanner.nextInt();
//System.out.println(res[n]);
System.out.println(res1[n]);
}
}
private static void sum1() {
// TODO Auto-generated method stub
for (int i = 2; i < res1.length; i++) {
res1[i] = (long)(res1[i-1] +pre1[i]);
//System.out.println(pre1[i]);
}
}
static int prime[] = new int[4000000];
static int visit[] = new int[4000000];
static int pre1[] = new int[4000000];
static long res1[] = new long[4000000];
private static void g() {
// TODO Auto-generated method stub
/* 对于visit[i*prime[j]] = 1 的解释: 这里不是用i的倍数来消去合数,
* 而是把 prime里面纪录的素数,升序来当做要消去合数的最小素因子。
*
* 对于 i%prime[j] == 0 就break的解释 :当 i是prime[j]的倍数时,i = kprime[j],
* 如果继续运算 j+1,i * prime[j+1] = prime[j] * k prime[j+1],
* 这里prime[j]是最小的素因子,当i = k * prime[j+1]时会重复,所以才跳出循环。
*/
// for (int i = 2; i < visit.length; i++) {
// pre1[i] = i;
// }
for (int i = 2; i < visit.length; i++) {
if(visit[i] == 0) {//i是素数
prime[++prime[0]] = i;//记录在prime数组中,这里prime[0]仅仅代表素数的个数。
pre1[i] = i;
}
for (int j = 1; j < prime[0] + 1 && i * prime[j] < visit.length; j++) {
visit[i*prime[j]] = 1;//prime[j]就是i*prime[j]的最小素因子
pre1[i*prime[j]] = prime[j];
if(i % prime[j] == 0) {
break;
}
}
}
}
}