之前一直觉得筛法不重要来着,自从一道题因为死在了筛法上后(没有选择合适的筛法,导致TLE)。。。
- 问n以内有多少个素数(n<=1e6)
埃氏筛法
时间复杂度O(n*loglogn)
内容:从2开始往后扫,表中留下来的最小数字是m,此时m是质数,将m的倍数全筛去。
int sieve(int n)
{
int tot=0;
for(int i=0;i<=n;i++) vis[i]=0;
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
prime[tot++]=i;
for(int j=i*i;j<=n;j+=i) vis[j]=1;
}
}
return tot;
}
但是我们发现会有一些重复删除的情况,比如12,会被2*6删一次,也会被3*4删一次。
优化之后,时间复杂度为O(n)(这里叫做优化筛)
void eselet(int n)
{
for(int i=0;i<=n;i++) vis[i]=0;
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
prime[tot++]=i;
}
for(int j=0;j<tot;j++)
{
LL k=prime[j]*i;
if(k>n) break;
vis[k]=1;
if(!i%prime[j]) break;
}
}
}
当前值i=p1p2p3…pn(p1<=p2<=p3<=..<=pn)*一个不大于i的素数prime[j],如果i是质数中不包括这个prime[j],那么i*prime[j]之前就没有出现过。prime[j]==p1,那么就跳出。
因为如果i=2*3=6,2*2*3标记,如果2*3*3标记了,那么i=9,3*3*2又会算一遍.
证明:1.不会重复筛
2.筛出了所有合数
1.证明
种1:当i是质数的时候,prime[j]*i没有出现过。
种2:当i=p2*...*pn(p2<=...<=pn),设p1=prime[j],那么i*p1=p1*p2...pn,是没有出现过的,因为不会有其他的会组成i*p1
2.证明
一个数唯一分解为:n=p1p2p3...pn(n为合数),那么当i=p2p3...pn的时候,会被筛掉
2.给定整数a和b,请问区间[a,b)内有多少个素数
(a < b<=1e12, b-a<=1e6)
这个不能直接用上面的方法,首先数组太大了,然后也会超时。
用区间筛法
首先,b以内的最小质因数一定不超过sqrt(b).如果有了sqrt(b)以内的素数表的话,就可以用埃氏筛法了。
void segment_sieve(LL n)
{
for(int i=0;i*i<b;i++) small[i]=0;
for(int i=0;i<b-a;i++) prime[i]=0;
for(int i=2;(LL)i*i<b;i++)
{
if(!small[i])
{
for(int j=i*i;(LL)j*j<b;j+=i) small[j]=1;//筛[2,sqrt(b))
for(LL j=(a+i-1)/i*i;j<b;j+=i) prime[j-a]=1;//筛[a,b)
}
}
}
//为什么不用优化筛优化,因为这样的话不一定到的了b
3。求n以内所有数的欧拉值
求单个欧拉值,n,要将n的质因数都算出来。
这里是求一个范围内的所有值,单个单个算会超时。用筛法求,这里用和上面的埃氏筛法很像
当前质数是谁的质因数,然后产生影响。
时间复杂度O(nloglogn)
void phi_table(int n)
{
for(int i=2;i<=n;i++) phi[i]=0;
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(!phi[i])
{
for(int j=i;j<=n;j+=i)
{
if(phi[j]==0) phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
}
}
4.求1-n内所有的莫比乌斯函数
d=p1*p2*p3...唯一分解
f(d)= 1 (p1p2p3..pn为互异素数,n为偶数或者d为1)
-1 (p1p2p3..pn为互异素数,n为奇数)
0 (其他)
求f(d),我们要看d是有哪些质数组成的。但是一个一个求会超时。
这里我们用的是优化筛
void mobious()
{
int tot=0;
miu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
prime[tot++]=i;
miu[i]=-1;
}
for(int j=0;j<tot;j++)
{
LL k=prime[j]*i;
if(k>maxn) break;
vis[k]=1;
if(i%prime[j]) miu[k]=-miu[i];
else break;
}
}
}