线性筛 (及其一大堆操作)

线性筛

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

线性筛的时间复杂度是严格 \(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];
        }
    }
}
posted @ 2019-09-15 08:18  yzhx  阅读(482)  评论(0编辑  收藏  举报