線性篩法及積性函數總結(歐拉函數、莫比烏斯函數、約數和函數、約數個數函數)


  線性篩法在數論中起着至關重要的作用,對於一部分求解有關積性函數的問題可以大大降低時間復雜度。線性篩法中,除了線性篩質數,所要篩的函數必須是積性函數,而線性篩這些函數的基礎也是線性篩質數。先來解釋一下什么是積性函數?積性函數就是指對於一個函數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形式的數時,這么做是不行的,我們就只能暴力求了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM