Skip to main content

筛法

Summary

筛法用于求素数与积性函数。埃拉托斯特尼筛法从每个素数出发把它的倍数标记为合数,时间复杂度为 O(n log log n);欧拉筛法即线性筛,让每个合数只被它的最小质因子筛去一次,达到 O(n),并能在筛的过程中递推积性函数。给出欧拉筛递推欧拉函数、莫比乌斯函数、约数个数函数与约数和函数四种实现,各自按当前数是否被最小质因子整除分两种转移。开头另附三种算法的复杂度对比表。

参考资料​

算法对比​

算法名称时间复杂度
朴素算法O(nn)O(n\sqrt n)
埃拉托斯特尼筛法O(nlog⁡log⁡n)O(n\log\log n)
欧拉筛法O(n)O(n)

埃拉托斯特尼筛法​

埃拉托斯特尼筛法(Sieve of Eratosthenes)简称埃氏筛法。

199 Bcpp
void sieve()
{
vis[0]=vis[1]=1;
for(int i=2;i*i<N;i++)
{
if(vis[i])continue;
for(int j=i*i;j<N;j+=i)vis[j]=1;
}
int cnt=0;
for(int i=2;i<N;i++)
{
if(vis[i])continue;
pri[++cnt]=i;
}
}

欧拉筛法​

欧拉筛法(Sieve of Euler)也称线性筛法,让每个合数只被它的最小质因子筛去一次,时间复杂度为 O(n)O(n)。它还能在筛的过程中递推出欧拉函数、莫比乌斯函数等积性函数。

素数​

205 Bcpp
void sieve()
{
vis[0]=vis[1]=1;
int cnt=0;
for(int i=2;i<N;i++)
{
if(!vis[i])pri[++cnt]=i;
for(int j=1;j<=cnt;j++)
{
if(i*pri[j]>=N)break;
vis[i*pri[j]]=1;
if(i%pri[j]==0)break;
}
}
}

数论函数​

φ(n)=n∏p∣np−1p\varphi(n)=n\prod_{p\mid n}\frac{p-1}{p}
328 Bcpp
void sieve()
{
vis[0]=vis[1]=1;
phi[1]=1;
int cnt=0;
for(int i=2;i<N;i++)
{
if(!vis[i])
{
pri[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt;j++)
{
if(i*pri[j]>=N)break;
vis[i*pri[j]]=1;
if(i%pri[j]==0)
{
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
phi[i*pri[j]]=phi[i]*phi[pri[j]];
}
}
}

例题​

给定一个范围 nn,有 qq 个询问,每次输出第 kk 小的素数。(n≤108,q≤106n\le10^8,q\le10^6)

给定 nn 组 l,rl,r 和最大值 mm,求区间 [l,r][l,r] 内质数的个数。(n≤1000,m≤106n\le1000,m\le10^6)