|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 王逗比666 于 2023-12-6 21:11 编辑
学习这个算法的时候看了这位大佬的题解
## 背景
最近遇到一个问题,需要判断大量的数是否是素数,暴力做法会TLE,所以在寻找解决办法的时候了解了欧拉筛法(线性筛法)
## 代码
这里以洛谷P3383为例
- cpp
- #include <iostream>
- #include <cstring>
- using namespace std;
- bool isPrime[114514191];
- int Prime[114514191];
- void solve(int n) {
- int cnt = 0;
- memset(isPrime, true, sizeof(isPrime));
- isPrime[1] = false;
- for(int i = 2; i <= n; i++) {
- if(isPrime[i]) {
- Prime[++cnt] = i;
- }
- for(int j = 1; j <= cnt && i * Prime[j] <= n; j++) {
- isPrime[i * Prime[j]] = false;
- if(i % Prime[j] == 0) break;
- }
- }
- }
- int main(void) {
- ios::sync_with_stdio(0);
- int n, q;
- cin >> n >> q;
- solve(n);
- while(q--) {
- int k;
- cin >> k;
- cout << Prime[k] << endl;
- }
- return 0;
- }
-
复制代码
## 时间复杂度: $ O(n)$
## 算法原理
欧拉筛的基本原理是利用因数分解的思想来筛去合数,对于任一合数均可被拆分为$最小质因数 \times 最大因数$,我们从2开始(因为2本身就素数,所以直接从它开始判断)枚举每一个素数,即$Prime[j]$,那么$i * Prime[j]$必定时一个合数,将其筛去。为了确保线性复杂度,我们期望每一个合数只被它的最小质因数筛去,这样每个数都指挥被筛一遍,为了确保这一点我们使用了if(i % Prime[j] == 0) break;由于$j$是从小到大开始枚举,当这个条件成立时,$Prime[j]$必定为i的最小质因数。
## 正确性证明(出自大佬博客)
设任一要被筛去的合数C的最小质因数为p,令B = C / p,则B的最小质因数一定不小于p($最小质因数 \times 最大质因数 = 合数$)所以当外层枚举到枚举$i = B$时,由于B的最小质因数一定不小于p,所以内层在结束break前一定会遇到p,(博客原话是:所以$i$在质数枚举至p之前一定不会break),这样就可以保证c会被筛去。
## The End |
|