大一寒假培训(八)——素数筛

素数筛的几种方法及相关原理

1.不知名的方法(O(nlogn))

筛法的思想是去除要求范围内所有的合数,剩下的就是素数了,而任何合数都可以表示为素数的乘积,则一个数的倍数为合数。

long long su[MAX],cnt;
bool isprime[MAX];
void prime()
{
	cnt=1;
	memset(isprime,1,sizeof(isprime));//初始化认为所有数都为素数 
	isprime[0]=isprime[1]=0;//0和1不是素数
	for(long long i=2;i<=MAX;i++){
		if(isprime[i]) su[cnt++]=i;
		for(long long j=i*2;j<=MAX;j+=i)
			isprime[j]=0;
	} 
}

2.埃氏筛(O(nloglogn))

合数的倍数一定会在筛素数倍数时候被筛掉,所以只筛素数就好,只把质数的倍速筛掉,就是找到一个质数,把它的倍数全部标记为合数。但是你会发现有的数字会被标记多次,比如12被2,3都标记,这样会浪费时间。

const int N=1e7+1;
int prime[N],b[N];
int cnt=0,max1;1e7;
int init()
{
	memset(b,1,sizeof(b));
	b[0]=b[1]=0;
	for(int i=2;i<=max1;i++)
		if(b[i]){
			prime[++cnt]=i;
			for(int j=2;j*i<=max1;j++)
				b[i*j]=0;
		}
	return 0;
}

3.线性筛(O(n))

埃氏筛可以优化,普通的线性筛法虽然大大缩短了求素数的时间, 但是实际上还是做了许多重复运算,比如2*3=6,在素数2的时候筛选了一遍, 在素数为3时又筛选了一遍。如果只筛选小于等于素数i的素数与i的乘积, 既不会造成重复筛选,又不会遗漏。时间复杂度几乎是线性的。

我们要筛1-n中的素数,然后先默认他们都是素数,最外层枚举 1-n的所有数,
如果它是素数,就加到素数表,
对于每一个枚举的i ,枚举素数表里的数,然后素数就会标记自己 i 倍的数不是素数,(素数的倍数不是素数)
枚举素数表什么时候停?枚举到 i 的最小质因子,标记完就可以停了,保证每个数只被他的最小质因子筛掉。
例如:外层 i=15 时,素数表里:2,3,5,7,11,13
215=30,把30筛掉;315=45,把45筛掉,因为15%3==0,退出里面的循环;
15是被3筛掉的,因为3是15的最小素因子。

const int N=1e7+1;
int prime[N],b[N];
int cnt=0,max1;1e7;
int init()
{
	memset(b,1,sizeof(b));
	b[0]=b[1]=0;
	for(int i=2;i<=max1;i++){
		if(b[i]) prime[++cnt]=i;
		for(int j=1;j<=cnt&&prime[j]*i<=max1;j++){
			b[prime[j]*i]=0;
			if(i%prime[j]==0) break;
		}
	}
}

判断素数

int su(long long n)//0为素数,1不为素数 
{
	int flag=0;
	for(int i=1;prime[i]<=sqrt(n*1.0);i++)
	if(n%prime[i]==0) {flag=1;break;}
	if(n==1) flag=1;
	return flag;
} 

唯一分解定理

概念:任意一个大于0的正整数都能被表示成若干个素数的乘积且表示方法是唯一的; 整理可以将相同素数的合并;可以得到 公式
n = P1 ^ a1 · P2 ^ a2 · ………… · Pn ^ an(P1 < P2 < ……Pn)

void getprimefactor(long long n)
{
	int cas=0;
	for(int i=0;prime[i]*prime[i]<=n;i++){
		while(!(n%prime[i])){
			factor[cas]++;
			n/=prime[i];
		}
		if(factor[cas]) cas++;
	}
	if(n>1) factor[cas]=1;
}

习题

纯素数

Description
李华再次发现有一种素数很奇特,它依次去掉最高位,剩下的仍为素数,例如:1223,3137等数。后来李华查阅资料知道这样的数叫纯素数。
现在给你一个整数能判断它是不是纯素数呢?
Input
测试数据有多组(不超过1e5组),每组只有一个整数N(1<=N<=1e6)
Output
对于每组数据,若N为纯素数,输出YES,否则输出NO
Sample Input
2
107
13903
Sample Output
YES
YES
NO

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+1;
int prime[N];
int b[N];
int cnt=0,max1=1e6;
int init()
{
	memset(b,1,sizeof(b));
	b[0]=b[1]=0;
	for(int i=2;i<=max1;i++)
		if(b[i]){
			prime[++cnt]=i;
			for(int j=2;i*j<=max1;j++)
				b[i*j]=0;
		}
}
int f(int n)
{
	char a[10];
	int j=0;
	while(n){
		a[j++]=n%10;
		n/=10;
	}
	for(int i=0;i<j;i++){	//要一位一位的判断
		int sum=0;
		for(int j=i;j>=0;j--)
			sum=sum*10+a[j];
		if(!b[sum]) return 0;
	}
	return 1;
}
int main()
{
	init();			//不要忘记在主函数打表
	int n;
	while(~scanf("%d",&n)){
		if(f(n)) cout<<"YES\n";
		else cout<<"NO\n";
	}
	return 0;
}

素数线性筛

Description
给定一个范围 n,有 q 个询问,每次输出第 k 小的素数。(2<=n<=4e7,1<=q<=1e4)
Input
第一行包含两个正整数 n,q,分别表示查询的范围和查询的个数。
接下来 q 行每行一个正整数 k,表示查询第 k 小的素数。
Output
输出 q 行,每行一个正整数表示答案。
Sample Input
100 5
1
2
3
4
5
Sample Output
2
3
5
7
11
Hint
本题卡内存,注意内存! 用bool 类型来定义素数标记

#include <bits/stdc++.h>
using namespace std;
const int N=4e7+1;
int prime[3000000];
bool b[40000001];
int cnt=0,max1=4e7;
int init()	//线性筛模板
{
	memset(b,1,sizeof(b));
	b[0]=b[1]=0;
	for(int i=2;i<=max1;i++){
		if(b[i]) prime[++cnt]=i;
		for(int j=1;j<=cnt&&prime[j]*i<=max1;j++){
			b[prime[j]*i]=0;
			if(!(i%prime[j])) break;
		}
	}
	return 0;
}
int main()
{
	ios::sync_with_stdio(0);
	init();
	int n,q,k;
	cin>>n>>q;
	while(q--){
		cin>>k;
		cout<<prime[k]<<endl;
	}
	return 0;
}

五十弦翻塞外声

Description
求一个数的因子和。
Input
输入一个正整数T(T<=10000),表示有T组数据,每组数据包括一个整数n(1<=n<=1e12)
Output
输出这个数的因子和
Sample Input
1
2
Sample Output
3

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
ll oh()	//可参考唯一分解定理的模板
{
	ll ans=1;
	for(ll i=2;i*i<=n;i++){
		if(!(n%i)){
			ll jet=1,sum=1;
			while(!(n%i))
			{jet*=i;sum+=jet;n/=i;}
			ans*=sum;
		}
	}
	if(n>1) ans*=(n+1);
	return ans;
}
int main()
{
	ios::sync_with_stdio(0);
	int t;
	ll ans;
	while(cin>>t)
		while(t--){
			cin>>n;
			ans=oh();
			cout<<ans<<endl;
		}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值