线性筛个人的一点小总结 和积性函数*

素数筛


普通写法: 获得[1,N]之间的所有素数
const int N=2e7+5; //注意一下,好想最大就2e7+9,如果是2e7+10就会炸,反正身边的电脑都是这样,一般不用开那么大
int prime[N];
// 获得[1,N]之间的素数,若prime[i]=0,表示i为素数
void getPrime()
{
	memset(prime,0,sizeof(prime));
	prime[0]=1;
	prime[1]=1;
	int temp=sqrt(N+0.5);
	for(int i=2;i<=temp;i++)
	{
		if(!prime[i])
		{
			for(int j=i*i;j<N;j+=i)
			{
				prime[j]=1;
			}
		}
	}
}
/*
使用:
i=1..N
if(!prime[i]) cout<<i<<endl;
*/
素数筛写法: 获得[1,N]之间的前prime[0]个素数
const int N=2e7+5;
int prime[N];
void getPrime()
{
	memset(prime,0,sizeof(prime));
	for(int i=2;i<=N;i++)
	{
		if(!prime[i])
		{
			prime[++prime[0]]=i;
		}
		for(int j=1;j<=prime[0]&&prime[j]*i<=N;j++)
		{
			prime[prime[j]*i]=1;
			if(i%prime[j]==0)
				break;
		}
	}
}
/*
使用:
i=1..prime[0]
cout<<prime[i]<<endl;
*/
解释:

程 序 的 流 程 : 程序的流程: :

首 先 i = 2.. N 如 果 p r i m e [ i ] 为 0 的 话 , 说 明 i 是 个 素 数 ( 1 ) 然 后 遍 历 j = 1.. p r i m e [ 0 ] , 同 时 p r i m e [ j ] ∗ i &lt; = N 那 么 p r i m e [ j ] ∗ i 为 合 数 , 标 记 p r i m e [ p r i m e [ j ] ∗ i ] 为 1 如 果 出 现 i % p r i m e [ j ] = 0 , 说 明 i 是 素 数 p r i m e [ j ] 的 倍 数 ( 即 i = k ∗ p r i m e [ j ] ) , 那 么 就 可 以 结 束 以 i 为 倍 数 的 合 数 标 记 了 ( 2 ) 首先 i =2..N\\如果prime[i]为0的话,说明i是个素数 (^1)\\ 然后遍历j=1..prime[0],同时prime[j]*i&lt;=N\\ 那么prime[j]*i为合数,标记prime[prime[j]*i] 为1\\ 如果出现i\%prime[j]=0,说明i是素数prime[j]的倍数(即i=k*prime[j]),那么就可以结束以i为倍数的合数标记了(^2) i=2..Nprime[i]0i(1)j=1..prime[0],prime[j]i<=Nprime[j]iprime[prime[j]i]1i%prime[j]=0,iprime[j](i=kprime[j])i(2)

( 1 ^1 1):
首先要说明的是i一定已经被标记过(具体为啥,也不会证明)\
[1,prime[0]]中表示已经存好的素数,\ 因为当遍历到i时,且i一定大于素数prime[prime[0] ],\此时prime[i]还存的是合数标记,若i不是合数,就会被存入到prime[++prime[0]] 中$
( 2 ^2 2):\
若 i 0 % p r i m e [ j ] = = 0 ( 即 i 0 = K ⋅ p r i m e [ j ] ) , 则 b r e a k 跳 出 后 , 还 没 有 进 行 处 理 的 就 是 p r i m e 数 组 中 下 标 j 之 后 的 素 数 下 标 i n d e x ∈ ( j ,   p r i m e [ 0 ]   ] , i 0 乘 以 它 们 , 都 可 以 转 换 成 k ∗ p r i m e [ j ] ∗ p r i m e [ i n d e x ] 的 形 式 , 也 就 是 说 当 i 2 = K ∗ p r i m e [ i n d e x ] ( 一 定 大 于 目 前 的 i 0 = K ∗ p r i m e [ j ] ) 时 , 同 时 遍 历 到 j 这 个 位 置 时 , 有 i 2 ∗ p r i m e [ j ] 就 等 于 i 0 ∗ p r i m e [ i n d e x ] ( = K ∗ p r i m e [ j ] ∗ p r i m e [ i n d e x ] ) 。 因 此 b r e a k 跳 出 后 , j 后 续 没 有 处 理 的 都 会 在 后 续 的 i 中 被 处 理 掉 若i_0\%prime[j]==0(即i_0=K\cdot prime[j]),则break跳出后,\\还没有进行处理的就是prime数组中下标j之后的素数下标index∈(j,\ prime[0]\ ],\\i_0乘以它们,都可以转换成k*prime[j]*prime[index]的形式,\\也就是说当i_2=K*prime[index](一定大于目前的i_0=K*prime[j])时,同时遍历到j这个位置时,\\有i_2*prime[j]就等于 i_0*prime[index](=K*prime[j]*prime[index])。\\因此break跳出后,j后续没有处理的都会在后续的i中被处理掉 i0%prime[j]==0(i0=Kprime[j]),breakprimejindex(j, prime[0] ],i0kprime[j]prime[index],i2=Kprime[index]i0=Kprime[j]ji2prime[j]i0prime[index](=Kprime[j]prime[index])breakji
( 3 ^3 3):\
如 何 证 明 每 个 数 只 被 标 记 了 一 次 , 素 数 不 会 被 标 记 , 只 要 考 虑 合 数 , 假 设 合 数 p r i m e [ j ] ∗ i 被 第 一 次 标 记 了 , 证 明 不 存 在 i 2 &gt; i , 使 得 i 2 ⋅ p r i m e [ j 2 ] = p r i m e [ j ] ⋅ i 成 反 证 : 因 为 i 2 &gt; i , 可 以 得 到 p r i m e [ j 2 ] &lt; p r i m e [ j ] , 若 等 式 成 立 就 必 须 满 足 i % p r i m e [ j 2 ] = = 0 , 而 当 i % p r i m e [ j 2 ] = = 0 时 , 此 时 已 b r e a k , 就 不 会 遍 历 到 比 p r i m e [ j 2 ] 还 要 大 的 p r i m e [ j ] 那 , 也 就 不 会 有 p r i m e [ j ] ∗ i 被 标 记 , 故 不 存 在 这 样 的 i 2 , 证 毕 如何证明每个数只被标记了一次,素数不会被标记,只要考虑合数,假设合数prime[j]*i被第一次标记了,证明不存在i_2&gt;i,使得i_2\cdot prime[j_2]=prime[j]\cdot i成 反证:因为i_2&gt;i,可以得到prime[j_2]&lt;prime[j],若等式成立就必须满足i\%prime[j_2]==0,而当i\%prime[j_2]==0时,此时已break,就不会遍历到比prime[j_2]还要大的prime[j]那,也就不会有prime[j]*i被标记,故不存在这样的i_2,证毕 prime[j]ii2>i,使i2prime[j2]=prime[j]ii2>i,prime[j2]<prime[j],i%prime[j2]==0,i%prime[j2]==0breakprime[j2]prime[j]prime[j]ii2,

模拟打个表:

i234567891011
prime[1..prime[0]]22,32,32,3,52,3,52,3,5,72,3,572,3,5,72,3,5,72,3,5,7,11
被标记的合数46,9810,15,251214,21,35,491618,27,45,632022,33,55,77,121
例题:(需要用到欧拉筛方面的知识)

欧拉函数筛


普通写法1: 获得一个数n的欧拉函数值
int euler_phi(int n)
{
	int ans=n;
	int tmp=sqrt(n+0.5);
	for(int i=2;i<=tmp;i++) if(n%i==0)
	{
		ans=ans/i*(i-1);
		while(n%i==0) n/=i;
	}
	if(n>1)
		ans=ans/n*(n-1);
	return ans;
}
普通写法2: 获得[1,n]所有的欧拉函数值
const int N=2e7+5;
int phi[N+1];
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])  //说明i是个素数,因此后面的操作的前提是i是素数
	{
		for(int j=i;j<=n;j+=i)
		{
			if(!phi[j]) phi[j]=j;
			phi[j]=phi[j]/i*(i-1); // 合数j中有i这个质因子,进行处理
		}
	}
}
欧拉函数筛写法: 获得[1,n]所有的欧拉函数值
下面的证明部分来自TCgogogo

在这里插入图片描述

const int N=2e7+5;
int prime[N+1];
int phi[N+1];
void get_eular()
{
	memset(prime,0,sizeof(prime));
	phi[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!prime[i])
		{
			prime[++prime[0] ]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=prime[0]&&i*prime[j]<=N;j++)
		{
			prime[prime[j]*i]=true;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]] = phi[i]*prime[j];
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}

莫比乌斯函数筛


下面的证明部分来自TCgogogo

在这里插入图片描述

const int N=2e7+5;
int prime[N+1];
int mob[N+1];
void Mobius()
{
	mob[1]=1;
	for(int i=2;i<N;i++)
	{
		if(!prime[i])
		{
			prime[++prime[0]]=i;
			mob[i]=-1;
		}
		for(int j=1;j<=prime[0]&& i*prime[j]<N;j++)
		{
			prime[i*prime[j]] =true;
			if(i%prime[j]==0)
			{
				mob[i*prime[j]]=0;
				break;
			}
			mob[i*prime[j]]=-mob[i];
		}
	 
	}
}

积性函数


以下内容来自百度百科

在这里插入图片描述在这里插入图片描述


参考资料:
  • https://kuangbin.github.io/2018/09/01/2018-ACM-ICPC-Nanjing-online-J/
  • https://blog.csdn.net/GodJing007/article/details/98075401
  • https://blog.csdn.net/Tc_To_Top/article/details/48025849
  • https://baike.baidu.com/item/%E7%A7%AF%E6%80%A7%E5%87%BD%E6%95%B0/8354949?fr=aladdin
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值