zoukankan      html  css  js  c++  java
  • 线性筛法及积性函数总结(欧拉函数、莫比乌斯函数、约数和函数、约数个数函数)

      线性筛法在数论中起着至关重要的作用,对于一部分求解有关积性函数的问题可以大大降低时间复杂度。线性筛法中,除了线性筛质数,所要筛的函数必须是积性函数,而线性筛这些函数的基础也是线性筛质数。先来解释一下什么是积性函数?积性函数就是指对于一个函数f,f(1)=1且对于任意两个互质的数x,y满足f(x)*f(y)=f(x*y)。而如果任意两个数x,y都满足以上等式,那么这个函数就是完全积性函数。

    常见且实用的积性函数有:

    1、l(x)=1,也叫单位函数,卷积单位元

    2、id(x)=x

    3、Φ(x),欧拉函数,1~x中与x互质的数的个数

    4、μ(x),莫比乌斯函数,当x=1时,函数值为1;当x为k个质数的一次幂乘积时,函数值为(-1)k;其他情况为0

    下面进入正题:

    为了方便描述,设f[]为对应积性函数,p[]为存质数的数组,p[j]是i的最小质因子,prime[j]与p[j]同义

    一、欧拉函数

    1、性质

    • 若p为质数,则Φ(p)=p-1
    • 若x与y互质,则Φ(x*y)=Φ(x)*Φ(y)
    • 若n为奇数,则Φ(2n)=Φ(n),(其实就是上一个性质的特殊情况)
    • 若n为质数p的k次幂,则Φ(n)=(p-1)*pk-1,(除p的倍数外都与n互质)
    • Σd|nΦ(d)=n

    2、线性筛

    当i为质数时,f[i]=i-1

    当i%p[j]!=0时,i与p[j]互质,f[i*p[j]]=f[i]*(p[j]-1)

    当i%p[j]==0时,设ak为i的一个质因子,Φ(i)=i*Π((ak-1)/ak),因此f[i*p[j]]=p[j]*f[i]

    #include<set>
    #include<map>
    #include<queue>
    #include<cmath>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    bool vis[10000010];
    int n;
    int f[10000010];
    int prime[1000000];
    int cnt;
    void find()
    {    
        f[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(!vis[i])
            {
                prime[++cnt]=i;
                f[i]=i-1;
            }
            for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    f[i*prime[j]]=prime[j]*f[i];
                    break;
                }
                else
                {
                    f[i*prime[j]]=(prime[j]-1)*f[i];
                }
            }
        }
    }
    int main()
    {
        scanf("%d",&n);
        find();
    }

    二、莫比乌斯函数

    1、性质

    • Σd|nμ(d)=ε(n),当n=1时,ε(n)=1;当n≠1时,ε(n)=0,(莫比乌斯反演用到的重要性质)

    2、线性筛

    当i为质数时,f[i]=-1

    当i%p[j]!=0时,说明i中没有p[j],那么f[i*p[j]]=-f[i]

    当i%p[j]==0时,f[i*p[j]]=0

    #include<set>
    #include<map>
    #include<queue>
    #include<cmath>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long 
    using namespace std;
    bool vis[10000010];
    int n;
    int f[10000010];
    int prime[1000000];
    int cnt;
    void find()
    {
        f[1]=1
        for(int i=2;i<=n;i++)
        {
            if(!vis[i])
            {
                prime[++cnt]=i;
                f[i]=-1;
            }
            for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    f[i*prime[j]]=0;
                    break;
                }
                else
                {
                    f[i*prime[j]]=-f[i];
                }
            }
        }
    }
    int main()
    {
        scanf("%d",&n);
        find();
    }

    三、约数个数函数

    1、性质

    对于任意一个大于1的正整数n都能质因数分解为n=p1a1*p2a2*……*pkak,f(n)=(1+a1)*(1+a2)*……*(1+ak)

    2、线性筛

    因为每个数由它最小的质因子筛出,因此要开一个辅助数组d[i]表示i最小质因子的次幂

    当i为质数时,f[i]=2,d[i]=1

    当i%p[j]!=0时,由于是积性函数,f[i*prime[j]]=f[i]*f[prime[j]]=2*f[i],d[i]=1

    当i%p[j]==0时,根据性质可知f[i*prime[j]]=f[i]/(d[i]+1)*(d[i]+2),d[i*prime[j]]=d[i]+1

    #include<set>
    #include<map>
    #include<queue>
    #include<cmath>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long 
    using namespace std;
    bool vis[10000000];
    int n;
    int f[10000000];
    int prime[10000000];
    int d[10000000];
    int cnt;
    void find()
    {
        f[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(!vis[i])
            {
                prime[++cnt]=i;
                f[i]=2;
                d[i]=1;
            }
            for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    f[i*prime[j]]=f[i]/(d[i]+1)*(d[i]+2);
                    d[i*prime[j]]=d[i]+1;
                    break;
                }
                else
                {
                    f[i*prime[j]]=2*f[i];
                    d[i*prime[j]]=1;
                }
            }
        }
    }
    int main()
    {
        scanf("%d",&n);
        find();
    }
    

     四、约数和函数

    1、性质

    对于任意大于1的正整数质因数分解,n=p1a1*p2a2*……*pkak

    f(n)=(1+p11+p12+……+p1a1)*(1+p21+p22+……+p2a2)*……*(1+pk1+pk2+……+pkak)

    2、线性筛

    设d[i]表示i最小质因子的各次幂之和

    当i为质数时,f[i]=i+1,d[i]=i+1

    当i%p[j]!=0时,f[i*p[j]]=f[i]*f[p[j]],d[i*p[j]]=1+p[j],积性函数性质

    当i%p[j]==0时,d[i*p[j]]=d[i]*p[j]+1,f[i*p[j]]=f[i]/d[i]*d[i*p[j]]

    #include<set>
    #include<map>
    #include<queue>
    #include<cmath>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long 
    using namespace std;
    bool vis[10000000];
    int n;
    ll f[10000000];
    int prime[10000000];
    ll d[10000000];
    int cnt;
    void find()
    {
        f[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(!vis[i])
            {
                prime[++cnt]=i;
                f[i]=i+1;
                d[i]=i+1;
            }
            for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    d[i*prime[j]]=d[i]*prime[j]+1;
                    f[i*prime[j]]=f[i]/d[i]*d[i*prime[j]];
                    break;
                }
                else
                {
                    f[i*prime[j]]=f[i]*f[prime[j]];
                    d[i*prime[j]]=1+prime[j];
                }
            }
        }
    }
    int main()
    {
        scanf("%d",&n);
        find();
    }
    

     总结:

      线性筛积性函数的方法一般都相同,都是利用积性函数互质可相乘的性质来筛。

      具体说一下方法:

    • 首先需要一个数组g[i]存i最小质因子的最高次幂的乘方,所要筛的积性函数还是用f[i]来表示,p[j]表示i的最小质因子。
    • 当i为质数时,直接求就好了。
    • 当i%p[j]!=0时,由积性函数性质直接相乘->f[i*prime[j]]=f[i]*f[prime[j]],d[i*prime[j]]=prime[j]
    • 当i%p[j]==0时,这时设t=i/d[i],t就与p[j]互质了,i*prime[j]=t*prime[j]*d[i],f[i*prime[j]]=f[t]*f[prime[j]*d[i]],d[i*prime[j]]=d[i]*prime[j]
    • 但当i=pk形式的数时,这么做是不行的,我们就只能暴力求了。
  • 相关阅读:
    mysql数据库的相关练习题及答案
    数据库一
    python的协程
    jquery的常用知识点
    diehard–让你的程序更健壮
    迷宫塔生成工具
    编程解决谁是凶手的问题
    ClojureScript实现xpath定位器生成-1
    使用ClojureScript进行chrome扩展开发
    AES CBC模式下的Padding Oracle解密
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9513533.html
Copyright © 2011-2022 走看看