zoukankan      html  css  js  c++  java
  • 线性筛 (及其一大堆操作)

    线性筛

    我已经掌握埃氏筛了 为什么还要学线性筛???

    线性筛的时间复杂度是严格 (O(N)) 的, 而埃氏筛的复杂度是 $ O(N * log_{2}( log_{2}(N) ) $

    看上去并没有快多少 实际也是, 但在处理一些大数据时,差距就凸显出来了

    算法思路

    概述:

    和埃氏筛类似的

    线性筛是通过枚举到的当前数字乘以某个比该数的最小质因子还小的质数来筛的

    (上面这句话有点长, 需要反复理解 暂时不能理解,也没关系,等下代码中我会详细讲的)

    这样就可以保证每次几乎不会出现重筛的情况,大大减少了循环次数

    代码:
    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define ll long long
    #define in inline
    #define get getchar()
    int read()
    {
        int t=0; char ch=get;
        while(ch<'0' || ch>'9') ch=get;
        while(ch<='9' && ch>='0') t=t*10+ch-'0',ch=get;
        return t;
    }
    const int _=1e7+6;
    int prime[_], tot, n; //tot是素数个数,prime是从小到大存放的素数数组 
    bool flag[_];//用来判断当前数已为素数,flag==1不是素数 
    int main()
    {
        n=read();
        for (re int i=2;i<=n;i++)  
        {
            if (!flag[i]) prime[++tot]=i; // 当前数没有被打上非素数标记 
            for (re int j=1;j<=tot&&prime[j]*i<=n;j++)
            {      //保证prime[j]在已确定的素数范围内 
                flag[prime[j]*i]=1; //打上非素数标记 
                if(i%prime[j]==0) break; //若当前prime[j]已是i的约数
                //剩下的不用重复处理,所以直接break 
            }
        }
        cout<<"TOT:  "<<tot<<endl;
        for (re int i=1;i<=tot;i++)
            cout<<prime[i]<<' ';
        return 0;
    }
    

    用线性筛在线性时间里求1~n的约数个数

    思路

    记录数字 i 的最小约数出现次数 minn[i]
    然后通过我们以前的知识 小学奥数 知道d(i)==(每个质因数的指数+1)的积
    然后利用线性筛的性质递推 minn[i] 与 d[i] 就好

    代码:

    
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define get getchar()
    #define in inline
    #define re register
    const int _=10000001;
    int minn[_],n,tot,prime[_],d[_];
    bool np[_];
    int main()
    {
        cin>>n;
        d[1]=1;
        for(re int i=2; i<=n; i++)
        {
            if(np[i]==0) prime[++tot]=i,d[i]=2,minn[i]=1;
            for(re int j=1;prime[j]*i<=n&&j<=tot;j++)
            {
                np[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    minn[i*prime[j]]=minn[i]+1;
                    d[i*prime[j]]=d[i]/(minn[i]+1)*(minn[prime[j]*i]+1);
                    break;
                }
                minn[i*prime[j]]=1;
                d[i*prime[j]]=d[i]*2;
            }
        }
        for( re int i=1;i<=n;i++)
            cout<<d[i]<<' '; 
    }
    

    线性筛求约数和

    n的约数和记作: (sigma(n))
    (n=prod_{i=1}^{k} p_i^{d_i})
    (sigma(n)=prod_{i=1}^{k}(sum_{j=0}^{d_i} p_i^j))
    (sigma)也是积性函数

    进入正题:

    low[i]为i的最小质因子所在的幂次((p_1^{d_1})
    sum[i]为i的最小质因子对答案的贡献((sum_{j=0}^{d_1} p_1^j)
    sigma[i]为答案
    然后通过线性筛枚举每个数的最小质因子来更新答案

    void Prepare()
    {
        sigma[1]=sum[1]=1;
        for(re int i=2;i<=n;i++)
        {
            if(!vis[i]) low[i]=p[++tot]=i,sum[i]=sigma[i]=i+1;
            for(re int j=1; j<=tot && p[j]*i<=n;j++)
            {
                vis[p[j]*i]=1;
                if(i%p[j]==0) {
                    low[i*p[j]]=low[i]*p[j];
                    sum[i*p[j]]=sum[i]+low[i*p[j]];
                    sigma[i*p[j]]=sigma[i]/sum[i]*sum[i*p[j]];
                    break;
                }
                low[i*p[j]]=p[j];
                sum[i*p[j]]=p[j]+1;
                sigma[i*p[j]]=sum[p[j]]*sigma[i];
            }
        }
    }
    

    线性筛欧拉函数

    $phi(n)$1~n中与n互质的数的个数,是积性函数

    void Prepare()
    {
        for(re int i=2;i<=n;i++)
        {
            if(!vis[i]) p[++tot]=i,phi[i]=i-1;
            for(re int j=1; j<=tot && p[j]*i<=n;j++)
            {
                vis[p[j]*i]=1;
                if(i%p[j])phi[p[j]*i]=phi[p[j]]*phi[i];
                else {phi[p[j]*i]=phi[i]*p[j];break;}
            }
        }
    }
    

    线性筛筛莫比乌斯函数

    void calc()
    {
        mu[1]=1;f[1]=1;
        for(re int i=2;i<=MAXN;i++)
        {
            if(!vis[i]) {mu[i]=-1; pri[++cnt]=i;}
            for(re int j=1;j<=cnt && i*pri[j]<=MAXN;j++)
            {
                vis[i*pri[j]]=1;
                if(i%pri[j]==0) break;
                else mu[i*pri[j]]=-mu[i];
            }
        }
    }
    
  • 相关阅读:
    Git提交错了不用慌,这三招帮你修改记录
    codeforces 1443D,解法简单,思维缜密的动态规划问题
    为什么优秀的人总是少数?我从天文学当中获得了一些启示
    本科入行可能吗?做到这3点,斩获BAT offer不是梦
    裸考了一次雅思,我居然学会了数据分析!
    有了Git这个功能,再也不需要依赖IDE了!
    职场中究竟什么是ownership,你是一个有ownership的人吗?
    为何跳槽不考虑腾讯?聊聊我和鹅厂的一点往事
    【Azure DevOps系列】Azure DevOps EFCore命令式脚本部署到SQL数据库
    Linux查看、开启、关闭防火墙操作
  • 原文地址:https://www.cnblogs.com/yzhx/p/11521073.html
Copyright © 2011-2022 走看看