歐拉篩可以認爲是埃篩的升級版。
埃篩的缺陷在於對於一個合數,有可能被篩多次,例如30=2*15=3*10=5*6。不難發現,如果我們用它的最小質因子來篩選,就可以保證每個合數只被篩選一次,這便是歐拉篩法。
而歐拉篩的擴展就在於它可以在篩素數的同時求積性函數。
積性函數定義:對於一個函數f(x)滿足:若gcd(a,b)=1,則f(a*b)=f(a)*f(b)。
求歐拉函數
int prime[maxn];
int mindiv[maxn];
int phi[maxn];
int cnt;
void solve(int n)
{
for(int i=2;i<=n;i++)
{
if(!mindiv[i])
{
mindiv[i]=prime[++cnt]=i; phi[i]=i-1;
}
for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
{
mindiv[k]=prime[j];
if(i%prime[j]==0)
{
phi[k]=phi[i]*prime[j];
break;
}
phi[k]=phi[i]*(prime[j]-1);
}
}
}
求最小質因數的指數
int prime[maxn];
int mindiv[maxn];
int minexp[maxn];
int cnt;
void solve(int n)
{
for(int i=2;i<=n;i++)
{
if(!mindiv[i])
{
mindiv[i]=prime[++cnt]=i;
minexp[i]=1;
}
for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
{
mindiv[k]=prime[j];
if(i%prime[j]==0)
{
minexp[k]=minexp[i]+1;
break;
}
minexp[i]=1;
}
}
}
求約數個數
int prime[maxn];
int mindiv[maxn];
int minexp[maxn];
int d[maxn];
int cnt;
void solve(int n)
{
for(int i=2;i<=n;i++)
{
if(!mindiv[i])
{
mindiv[i]=prime[++cnt]=i;
minexp[i]=1;
d[i]=2;
}
for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
{
mindiv[k]=prime[j];
if(i%prime[j]==0)
{
minexp[k]=minexp[i]+1;
d[k]=d[i]/(minexp[i]+1)*(minexp[k]+1);
break;
}
minexp[i]=1;
d[k]=d[i]*d[prime[j]];
}
}
}
求約數個數還有個野路子
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i)
d[j]++;
雖然速度不及歐拉篩,但它簡單好寫啊。
求約數和
int prime[maxn];
int mindiv[maxn];
int sumd[maxn];
int f1[maxn];//f1[i]=sigma(mindiv[i]^(0..minexp[i]))
int f2[maxn];//f2[i]=mindiv[i]^minexp[i]
int cnt;
void solve(int n)
{
for(int i=2;i<=n;i++)
{
if(!mindiv[i])
{
mindiv[i]=prime[++cnt]=i;
sumd[i]=1+i;
f1[i]=1+i;
f2[i]=i;
}
for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
{
mindiv[k]=prime[j];
if(i%prime[j]==0)
{
f2[k]=f2[i]*prime[j];
f1[k]=f1[i]+f2[k];
sumd[k]=sumd[i]/f1[i]*f1[k];
break;
}
sumd[k]=sumd[i]*sumd[prime[j]];
f1[k]=1+prime[j];
f2[k]=prime[j];
}
}
}
求莫比烏斯函數
int prime[maxn];
int mindiv[maxn];
int miu[maxn];
int cnt;
void solve(int n)
{
miu[1]=1;
for(int i=2;i<=n;i++)
{
if(!mindiv[i])
{
mindiv[i]=prime[++cnt]=i;
miu[i]=-1;
}
for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
{
mindiv[k]=prime[j];
if(i%prime[j]==0)
{
miu[k]=0;
break;
}
miu[k]=-miu[i];
}
}
}
以上都是比較常見的積性函數,用篩法求積性函數的本質是根據積性函數的定義和運算式,利用篩法的特點,構造合適的轉移方法。
這裏給出基本模板
void solve(int n)
{
v[1]=1;
for(int i=2;i<=n;i++)
{
if(!v[i])
{
prime[++cnt]=i;
.....//素數的積性函數值
}
for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
{
v[i*prime[j]]=1;
.....//公共部分
if(i%prime[j]==0)
{
.....//k和k的最小素因子的處理
break;
}
.....//k和比k的最小素因子小的素數的處理
}
}
}