筛法学习

朴素算法:对于一个数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相同的道理。

posted @ 2021-07-15 10:59  lei_yu  阅读(47)  评论(0编辑  收藏  举报