• 三种素数筛


    P3383 【模板】线性筛素数

    来源:洛谷 https://www.luogu.com.cn/problem/P3383

     这题考 [素数筛] ,但数据范围很毒瘤,100%数据n=10^8,意味着只有 [线性筛] 才能AC

    以下提供3种素数筛法

    ①朴素算法(时间复杂度O(n*sqrt(n))

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int read(){
     4     int flag=0,x=0;
     5     char a=getchar();
     6     while(a<'0'||a>'9'){
     7         if(a=='-')flag=1;
     8         a=getchar();
     9     }
    10     while(a>='0'&&a<='9'){
    11         x=x*10+a-'0';
    12         a=getchar();
    13     }
    14     return flag?-x:x;
    15 }
    16 void write(int x){
    17     if(x<0){
    18         putchar('-');
    19         x=-x;
    20     }
    21     if(x>9){
    22         write(x/10);
    23     }
    24     putchar(x%10+'0');
    25 }
    26 bool pd(int x){
    27     for(int i=2;i<=sqrt(x);i++){
    28         if(x%i==0)return 0;
    29     }
    30     return 1;
    31 }
    32 int ans[100000005],k; 
    33 int main(){
    34     int n=read();
    35     int m=read();
    36     for(int i=2;i<=n;i++){
    37         if(pd(i))ans[++k]=i;
    38     }
    39 //    for(int i=1;i<=k;i++){
    40 //        write(ans[i]);
    41 //        putchar(' ');
    42 //    }
    43     for(int i=1;i<=k;i++){
    44         int a=read();
    45         write(ans[a]);
    46         puts(" ");
    47     }
    48     return 0;
    49 }

    在这道题中显然时间复杂度是 O(10^12)

    垃圾代码,砸掉!

    ②艾氏筛(时间复杂度O(n log log(n))

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int ss[100000005],ans[100000005],k;
     4 int read(){
     5     int flag=0,x=0;
     6     char a=getchar();
     7     while(a<'0'||a>'9'){
     8         if(a=='-')flag=1;
     9         a=getchar();
    10     }
    11     while(a>='0'&&a<='9'){
    12         x=x*10+a-'0';
    13         a=getchar();
    14     }
    15     return flag?-x:x;
    16 }
    17 void write(int x){
    18     if(x<0){
    19         putchar('-');
    20         x=-x;
    21     }
    22     if(x>9){
    23         write(x/10);
    24     }
    25     putchar(x%10+'0');
    26 }
    27 int main(){
    28     int n=read();
    29     int m=read();
    30     for(int i=2;i<=n;i++){
    31         if(!ss[i]){
    32             ans[++k]=i;
    33             for(int j=i;i*j<=n;j++){
    34                 ss[i*j]=1;
    35             }
    36         }    
    37     }
    38 //    for(int i=1;i<=k;i++){
    39 //        write(ans[i]);
    40 //        putchar(' ');
    41 //    }
    42     for(int i=1;i<=m;i++)
    43     {
    44         int a=read();
    45         write(ans[a]);
    46         puts(" ");
    47     }
    48     return 0;
    49 }

    O(n log log(n))已经是很优秀的运算速度了!

    但很可惜,仍然有重复运算的情况出现占时间

    例如:12;

    在i=2,j=6时已经被标记过了;

    但在i=3,j=4时又被标记了一次

    24就更不用说了(24:你礼貌吗

    而且!

    这种算法交上去出现了玄学错误

     ③欧拉筛(时间复杂度O(n))

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int ss[100000005],ans[100000005],k;
     4 int read(){
     5     int flag=0,x=0;
     6     char a=getchar();
     7     while(a<'0'||a>'9'){
     8         if(a=='-')flag=1;
     9         a=getchar();
    10     }
    11     while(a>='0'&&a<='9'){
    12         x=x*10+a-'0';
    13         a=getchar();
    14     }
    15     return flag?-x:x;
    16 }
    17 void write(int x){
    18     if(x<0){
    19         putchar('-');
    20         x=-x;
    21     }
    22     if(x>9){
    23         write(x/10);
    24     }
    25     putchar(x%10+'0');
    26 }
    27 int main(){
    28     int n=read();
    29     int m=read();
    30     ss[1]=1;
    31     for(int i=2;i<=n;i++){
    32         if(!ss[i]){
    33             ans[++k]=i;
    34         }
    35         for(int j=1;j<=k&&i*ans[j]<=n;j++){
    36             ss[i*ans[j]]=1;
    37         }
    38     }
    39 //    for(int i=1;i<=k;i++){
    40 //        write(ans[i]);
    41 //        putchar(' ');
    42 //    }
    43     for(int i=1;i<=m;i++)
    44     {
    45         int a=read();
    46         write(ans[a]);
    47         puts(" ");
    48     }
    49     return 0;
    50 }

    时间复杂度是极为优秀的O(n),因此也叫~~~线性筛~~~!!!

    但其实------

    你们若是把上边的代码copy上去是会-------

     TLE的!

    为什么呢?

    因为它并不是一个有灵魂的欧拉筛代码

    缺少了break偷懒的欧拉筛不是一个好的欧拉筛

    欧拉筛的核心代码:

    if(i%ans[j]==0)break;

    一行代码学问大了去了

    首先

    原理

    其中的Prime数组就是上边代码的ans了

     

    正确性(所有合数都会被标记)证明

    线性复杂度证明

     题解原地址:https://www.luogu.com.cn/problem/solution/P3383

    就是置顶的那篇

    最后

    完整AC代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int ss[100000005],ans[100000005],k;
     4 int read(){
     5     int flag=0,x=0;
     6     char a=getchar();
     7     while(a<'0'||a>'9'){
     8         if(a=='-')flag=1;
     9         a=getchar();
    10     }
    11     while(a>='0'&&a<='9'){
    12         x=x*10+a-'0';
    13         a=getchar();
    14     }
    15     return flag?-x:x;
    16 }
    17 void write(int x){
    18     if(x<0){
    19         putchar('-');
    20         x=-x;
    21     }
    22     if(x>9){
    23         write(x/10);
    24     }
    25     putchar(x%10+'0');
    26 }
    27 int main(){
    28     int n=read();
    29     int m=read();
    30     ss[1]=1;
    31     for(int i=2;i<=n;i++){
    32         if(!ss[i]){
    33             ans[++k]=i;
    34         }
    35         for(int j=1;j<=k&&i*ans[j]<=n;j++){
    36             ss[i*ans[j]]=1;
    37             if(i%ans[j]==0)break;
    38         }
    39     }
    40 //    for(int i=1;i<=k;i++){
    41 //        write(ans[i]);
    42 //        putchar(' ');
    43 //    }
    44     for(int i=1;i<=m;i++)
    45     {
    46         int a=read();
    47         write(ans[a]);
    48         puts(" ");
    49     }
    50     return 0;
    51 }

    设一合数 CC(要筛掉)的最小质因数是 p_1p1,令 B = C / p_1B=C/p1C = B × p_1C=B×p1),则 BB 的最小质因数不小于 p_1p1(否则 CC 也有这个更小因子)。那么当外层枚举到 i = Bi=B 时,我们将会从小到大枚举各个质数;因为 i = Bi=B 的最小质因数不小于 p_1p1,所以 ii 在质数枚举至 p_1p1 之前一定不会break,这回CC 一定会被 B × p_iB×pi 删去。

    核心:亲爱的 BB 的最小质因数必不小于 p_1p1

    例:315 = 3 × 3 × 5 × 7315=3×3×5×7,其最小质因数是 33。考虑 i = 315 / 3 = 105i=315/3=105 时,我们从小到大逐个枚举质数,正是因为 ii 的最小质因数不会小于 33(本例中就是 33),所以当枚举 j = 1 (Prime[j] = 2)j=1(Prime[j]=2) 时,ii 不包含 22 这个因子,也就不会break,直到 Prime[j] = 3Prime[j]=3 之后才退出。

    当然质数不能表示成“大于1的某数×质数”,所以整个流程中不会标记。

  • 相关阅读:
    为什么我们从Yarn切换到pnpm
    🔑 最佳密码长度是多少?
    vue + ArcGIS 地图应用系列二:加载地图
    vue + ArcGIS 地图应用系列一:arcgis api本地部署(开发环境)
    玩转 GitHub 的几个小技巧
    在 Array.some 中正确使用 async
    如何正确的在 Array.map 使用 async
    一道关于JavaScript 代码执行顺序的面试题解析
    Git 常用命令及应用这一篇就够了(新手向)
    VUE 子组件向父组件传值 , 并且触发父组件方法(函数)
  • 原文地址:https://www.cnblogs.com/TFLSc1908lzs/p/15164528.html
Copyright © 2020-2023  润新知