25-最小质因子之和

题目描述

定义 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;
                }
            }
        }
    }

}

你可能感兴趣的:(蓝桥杯备战题,算法,数据结构,java,蓝桥杯)