洛谷 P3383 【模板】线性筛素数
筛素数就是把不是素数的筛掉,剩下的就是素数。
本题数据量较大,用埃氏筛法会超时,因为埃氏筛法不是线性的,比如 6 会被 2 筛一次,还会被 3 筛一次,而欧拉线性筛的关键在于:每个合数只被它 最大的非自身的因数 (或者说是它的 最小质因数 ) 筛掉。
每一个合数 = 最小质因数 * 最大的非自身的因数。
比如 6 只会被 3 (最小质因数 2 )筛掉;12 只会被 6 (最小质因数 2 )筛掉。
i的值 | 被筛掉的数 |
---|---|
2 | 4(2 % 2 == 0 break) |
3 | 6、9(3 % 3 == 0 break) |
4 | 8(4 % 2 == 0 break) |
5 | 10、15、25(5 % 5 == 0 break) |
6 | 12(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();
}
}