线性筛

参考视频

线性筛是用来求特定函数f(x)的一种方法

素数筛

P3383 【模板】线性筛素数

  • 问题:求小于等于 n 有多少个素数分别是什么

朴素筛

  • 暴力枚举
int is_prime(int n)
{
	if(n==1)return 0;
	for(int i=2;i<=sqrt(n);i++)
	if(n%i==0)return 0;
	return 1;
}

埃氏筛-Eratosthenes 筛法 (埃拉托斯特尼筛法)

  • 核心:从头开始,找到一个素数,把它的倍数全部标记(相当于筛掉)

int Eratosthenes(int n)
{
	int cnt=0;
	for(int i=0;i<=n;i++)isprime[i]=1;
	isprime[0]=isprime[1]=0;
	for(int i=2;i<=n;i++)
	{
		if(isprime[i])prime[cnt++]=i;
		for(int j=i*2;j<=n;j+=i)isprime[j]=0;
	} 
	return cnt;
}

Euler 筛法 (欧拉筛法)

  • 思路:对埃氏筛的一种优化
    例如:
    6=2 * 3
    用埃氏筛时,筛除6的时候,i=2时筛了一次,i=3时又筛了一次,出现了重复筛的现象
  • 任何合数都能写成一个素数乘一个数,所以任何合数都有一个最小的质因数,用这个最小质因数来判断什么时候不用继续筛下去,即i (当前数) >最小质因数
  • 怎么判断?
    if(i%prime[j]==0) 则 i=k * prime[j]
    如果j继续++的话,某合数= i * prine[j+1] = prime[j] * k * prime[j+1]
    此时prime[i+1]就不时最小素质数
    该合数=prime[j] * ( k * prime[j+1] )
    所以i=k*prme[j+1]就会重复,此处用break可以提高代码
    效率

int Euler(int n)
{
	vis[0]=vis[1]=1;
	int cnt=0;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])prime[cnt++]=i;
		for(int j=0;j<cnt&&prime[j]*i<=n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
	return cnt;
}

线性筛求欧拉函数

预备知识

  • 欧拉函数:对正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目(因此φ(1)=1)
  • 积性函数:对于任意互质的整数a和b有性质f(a*b)=f(a)*f(b)的数论函数。
    (这里的)欧拉函数和 (下面的) 莫比乌斯函数都是积性函数
    if(i%prime[j]!=0) phi[i*prime[j]] = phi[i]*phi[prime[j]]
  • 定理gcd(a,b)==1 则 gcd(a,a+b)==1
    1到i中有x个数与i互质 则 i+1到2i中也有x个数与i互质
    if(i%prime[j]==0) phi[i*prime[j]] = phi[i]*prime[j];

代码

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=5e7;
int vis[N],phi[N],prime[N];
//欧拉函数是小于或等于n的正整数中与n互质的数的数目
void phi_table(int n)
{
	phi[1]=1;int cnt=0;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			prime[cnt++]=i;
			phi[i]=i-1;
		}
		for(int j=0;j<cnt&&prime[j]*i<=n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);//phi[i*prime[j]]=phi[i]*phi[prime[j]]
		}
	}
}
int main()
{
	int n;
	scanf("%d",&n);
	phi_table(n);
	for(int i=1;i<=n;i++)printf("%d  ",phi[i]);
	printf("\n");
	return 0;
}

线性筛求莫比乌斯函数

  • 莫比乌斯函数:x=p1k1p2k2
    f(x)=0 存在ki>=2
    否则 f(x)=(-1)n n为ki=1的个数即x的质因数的个数
  • 代码
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=5e7;
int vis[N],mo[N],prime[N];
void mobius(int n)
{
	mo[1]=1;int cnt=0;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			prime[cnt++]=i;
			mo[i]=-1;
		}
		for(int j=0;j<cnt&&prime[j]*i<=n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				mo[i*prime[j]]=0;
				break;
			}
			else mo[i*prime[j]]=-mo[i];
		}
	}
}
int main()
{
	int n;
	scanf("%d",&n);
	mobius(n);
	for(int i=1;i<=n;i++)printf("%d  ",mo[i]);
	printf("\n");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值