Problem Description
小明对数的研究比较热爱,一谈到数,脑子里就涌现出好多数的问题,今天,小明想考考你对素数的认识。
问题是这样的:一个十进制数,如果是素数,而且它的各位数字和也是素数,则称之为“美素数”,如29,本身是素数,而且2+9 = 11也是素数,所以它是美素数。
给定一个区间,你能计算出这个区间内有多少个美素数吗?
Input
第一行输入一个正整数T,表示总共有T组数据(T <= 10000)。
接下来共T行,每行输入两个整数L,R(1<= L <= R <= 1000000),表示区间的左值和右值。
Output
对于每组数据,先输出Case数,然后输出区间内美素数的个数(包括端点值L,R)。
每组数据占一行,具体输出格式参见样例。
input
3
1 100
2 2
3 19
output
Case #1: 14
Case #2: 1
Case #3: 4
用一般思路想,对于每个区间l,r,可以for一遍,判断数i是否为素数,并且i的每一位的和是否也是素数,若满足条件ans++。
那么考虑到判断素数的次数很大,可以先预处理出1~ maxn的每一个数字是否为素数。
埃氏筛
埃氏筛的思路很简单,核心的思想是 任意整数x的倍数都不是质数。那么我们从2到maxn枚举这个x,对于每一个x枚举其倍数,得到的i*x一定为合数,用桶的思想将其标记掉就可以了。但是这里注意的是第二步枚举倍数时是从x开始的,也就是筛选的是 x ∗ x , ( x + 1 ) ∗ x , ( x + 2 ) ∗ x . . . x*x,(x+1)*x,(x+2)*x... x∗x,(x+1)∗x,(x+2)∗x...直到乘积大于等于maxn。实际上,小于 x 2 x^2 x2的倍数已经由比x更小的数筛过了。举个例子,当 x = 5 x=5 x=5的时候,我们应该从 5 ∗ 5 5*5 5∗5开始筛数,因为 2 ∗ 5 , 3 ∗ 5 , 4 ∗ 5 2*5,3*5,4*5 2∗5,3∗5,4∗5已经由 2 , 3 , 2,3, 2,3,和 2 ∗ 10 2*10 2∗10的2 所标记了
void pre_prime()//埃氏筛
{
memset(is_prime,true,sizeof is_prime);
is_prime[1]=0;
for(int i=2;i<maxn;i++)
{
if(is_prime[i]==0)continue;//合数跳过
for(int j=i;j<maxn/i;j++)
{
is_prime[j*i]=0;//标记合数
}
}
return ;
}
本来以为埃氏筛可以水过去的,结果还是WA了,据说埃氏筛的时间复杂度是 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
欧拉筛
埃氏筛在筛的过程中还是会重复标记一个数,比如第一轮用2筛的时候会标记6,8,10…是合数;在第二轮用3筛的时候会标记6,9,12…是合数。这里6就被重复筛了两次。也就造成了冗余操作。
那么欧拉筛的想法是让每一个数只被标记一次,而未被标记的数则会检测出是质数,那么就可以做到时间复杂度 O ( n ) O(n) O(n)了,也就是线性筛素数
首先,算数基本定理
算数基本定理: 任何一个大于1的正整数都能唯一分解为有限个质数的乘积。 N = p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p m c m N=p_1^{c_1}*p_2^{c_2}*...*p_m^{c_m} N=p1c1∗p2c2∗...∗pmcm其中 c i c_i ci为正整数, p i p_i pi为质数,而且满足 p 1 < p 2 < . . . < p m p_1
p1<p2<...<pm
欧拉筛的主要思想就是利用一个数等于最小质因数*因数,那么在筛的过程中,只要确保这个数是由最小的质因数筛掉的话就可以保证每个合数只被筛去一次。
详细参考博客 欧拉筛筛素数
在筛出1~ maxn的素数之后,对区间内符合条件的美素数的个数进行一波前缀和,预处理出从1~ maxn的符合条件素数的个数和,在询问的时候输出ans[r]-ans[l-1]即可。
#include
#include
#include
using namespace std;
const int maxn=1e6+7;
bool is_prime[maxn];
int prime[maxn];
int ans[maxn];
void pre()//欧拉筛
{
int cnt=0;
memset(is_prime,1,sizeof is_prime);
prime[1]=0;
for(int i=2;i<maxn;i++)
{
if(is_prime[i])
{
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&i*prime[j]<maxn;j++)
{
is_prime[i*prime[j]]=0;
if(i%prime[j]==0)
{
break;
}
}
}
}
bool check(int x)
{
int sum=0;
while(x)
{
sum+=x%10;
x/=10;
}
return is_prime[sum];
}
int main()
{
//pre_prime();
pre();
int T;
cin>>T;
ans[1]=0;
for(int i=2;i<maxn;i++)
{
ans[i]=ans[i-1];
if(is_prime[i]&&check(i))
{
ans[i]++;
}
}
for(int t=1;t<=T;t++)
{
int l,r;
cin>>l>>r;
cout<<"Case #"<<t<<": "<<ans[r]-ans[l-1]<<endl;
}
return 0;
}