欧拉筛可以认为是埃筛的升级版。
埃筛的缺陷在于对于一个合数,有可能被筛多次,例如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的最小素因子小的素数的处理
}
}
}