洛谷 P3383 【模板】线性筛素数 欧拉线性筛

洛谷 P3383 【模板】线性筛素数
筛素数就是把不是素数的筛掉,剩下的就是素数。
本题数据量较大,用埃氏筛法会超时,因为埃氏筛法不是线性的,比如 6 会被 2 筛一次,还会被 3 筛一次,而欧拉线性筛的关键在于:每个合数只被它 最大的非自身的因数 (或者说是它的 最小质因数 ) 筛掉。
每一个合数 = 最小质因数 * 最大的非自身的因数。
比如 6 只会被 3 (最小质因数 2 )筛掉;12 只会被 6 (最小质因数 2 )筛掉。

i的值被筛掉的数
24(2 % 2 == 0 break)
36、9(3 % 3 == 0 break)
48(4 % 2 == 0 break)
510、15、25(5 % 5 == 0 break)
612(6 % 2 == 0 break)

例:315 = 3 * 3 * 5 * 7。
315 可能用 3 * 105 或 5 * 63 或 7 * 45 三种方式筛掉。但是 i = 45 时(45 % 3 == 0 break 了),i = 63 时(63 % 3 == 0 break 了),根本到达不了 7 或 5,所以 315 只有一种情况会被筛掉。

import java.io.*;
import java.util.Arrays;
/**
 * @author wangshaoyu
 */
public class Main {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static int nextInt() throws IOException {
        st.nextToken();
        return (int) st.nval;
    }

    public static void main(String[] args) throws IOException {

        int n = nextInt(); // 查询范围
        int q = nextInt(); // 查询次数
        boolean[] isPrime = new boolean[n + 5];
        Arrays.fill(isPrime, true);  // 初始化都是素数
        int[] prime = new int[9000000]; // 存放素数的数组要开大一点
        int cnt = 0;

		// 外层循环枚举最大的非自身的因数,内层循环枚举最小质因子
        for (int i = 2; i <= n; i++) {
      		// 如果当前这个素数经过前面的筛选后依然为true,那它就是素数
            if (isPrime[i] == true) {
                prime[cnt] = i;
                cnt++;
            }
            // 枚举每一个已知的素数,注意不要越界
            for (int j = 0; j < cnt && i * prime[j] <= n; j++) {
                isPrime[i * prime[j]] = false;
                // 这里是为了保证每一个合数只会被它的最小质因数筛掉
                // 这里筛掉的合数是 i * prime[j], i 和 prime[j] 都是该合数的因子,
                // 如果 prime[j] 是 i 的因子,那么prime[j] 也会是 i * prime[j] 的因子
                // 并且因为我们是从小到大枚举已经求出的素数,所以第一个取余为 0 的肯定是 i * prime[j] 的最小质因子。
                if (i % prime[j] == 0) {
                    break;
                }
            }
        }
        while (q-- > 0) {
            int k = nextInt();
            pw.println(prime[k - 1]);
        }
        pw.flush();
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值