筛法学习
朴素算法:对于一个数x,枚举小于 \(\sqrt x\) 的数看他有没有质数即可
因为没有大于\(\sqrt x\)的因数
筛法1:从2开始,将每个质数的倍数都标记成合数,
筛法2:对于一个数\(i\),只用它的最小质因子筛它一次。
对于每一个数,都将它乘以某一个质数\(prime[j]\)得到一个新的合数\(i*prime[j]\)
为了保证这个合数只被它的最小质因子筛一次,我们在枚举到某个\(prime[j]\)使得\(i\)是\(prime[j]\)的倍数时即\(i/prime[j]=0\)时就可以退出\(j\)的循环。
这样可以保证在\(i/prime[j]=0\)及之前,都可以有最小质因子prime[j],且即使在此时,\(i*prime[j]\)依然有最小质因子\(prime[j]\),因为\(i/prime[j]\)必然是一个合数或是一个比\(prime[j]\)更大的质数,否则在找到j之前就会找到\(\frac{i}{ i/prime[j]} =0\)了
但是在这之后,由于i有一个更小的质因子\(prime[j]\),筛到的数就不再有最小质子\(prime[j']\)了
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,prime[100000001],b[100000001],cnt,q;
int main()
{
//freopen("test.txt","w",stdout);
cin>>n>>q;
for(int i=2;i<=n;i++)
{
if(!b[i])prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
b[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
int x;
for(int i=1;i<=q;i++)
{
scanf("%d",&x);
printf("%d\n",prime[x]);
}
}
筛法还可以有很多作用:
1、求1~n中的所有欧拉函数\(\phi(i)\)。
对于一个数\(i\)和一个质数\(prime[j]\)且它们互质,根据欧拉函数是一个积性函数,我们可以得到\(\phi(i*prime[j])=\phi(i)\phi(prime[j])\)。
对于一个质数\(prime[j]\)和它的倍数\(i\),\(\phi(i*prime[j])=\phi(i)prime[j]\)
2、求1~n的因数个数
对于一个数\(i\)和一个质数\(prime[j]\)且它们互质,唯一分解可得\(f[i*prime[j]]=2f[i]\)
对于一个质数\(prime[j]\)和它的倍数\(i\),设\(i=kprime[j]\),故\(i*prime[j]=k*prime[j]^2\),于是对k进行质因子的唯一分解,然后我们就可以得到\(f[i*prime[j]]=3f[k]\)
3、求1~n的因数和
和2相同的道理。
本文来自博客园,作者:lei_yu,转载请注明原文链接:https://www.cnblogs.com/lytql/p/15014514.html