质数又称素数,指在大于1的自然数中,只能被1和本身整除的数,也可定义为只有1和本身两个因数的数。而半质数的定义是这样的:若对于一个正整数N,恰好能够分解成两个质数的乘积,它就被称为半质数。比如,4=2*2,15=3*5都是半质数,12不是半质数,它的质因子分解式为12=2*2*3,分解出的质数共有3个,其中有2个质数2,1个质数3。
(jzxx. 2120 )
输入
输入数据仅有一行包含两个用空格隔开的正整数 S 和 E,其中 2≤S≤E<5000000。
输出
输出数据仅有一行包含一个整数表示在 S 到 E 之间共有多少个半质数。
样例输入
4 26
样例输出
10
提示
样例解释
在 4 到 26 之间共有 10 个半质数,分别是 4, 6, 9, 10, 14, 15, 21, 22, 25, 26。
数据范围
30%的数据满足:2≤S≤E<500
60%的数据满足:2≤S≤E<50000
100%的数据满足:2≤S≤E<5000000
来源/分类
常州市2014“信息与未来”夏令营选拔赛
题目地址http://oj.jzxx.net/problem.php?id=2120
【思路】
本题数据量大,而且限时1秒、限内存32M,32M空间大约能开整型数组800万左右(32×1024×1024/4=838万),直接暴力枚举、判断不可取,应考虑优化算法。容易想到的生成法,枚举两个乘数,只要乘积在给定区间内,就是一个解,最后输出解的个数即可。这个算法需要进行细节优化。
首先,筛法打素数表,大幅节省判素数的时间。其原理是,开一个布尔型数组pm[],其值表示下标i是否为素数,先赋初值为0,假定都是素数;那么如果2是素数,则2的所有倍数都不是素数,要划去,标记为1。
其次,限定两个乘数的数值范围。第1个乘数i是2 ~ E,再大则乘积大于E;第2个乘数j是i ~ E/2,此处限定j大于等于i,是为避免重复,有等于号是因为两个乘数可以相等;j最大是E/2,是因为有拆成2乘另一个素数的情况。
第三,根据乘积结果控制循环。如果比左端点S小,就继续枚举j;如果比右端点E大,则需要退出对j的枚举循环,因为再枚举也必定比E大。
#include <cstdio>
#include <cmath>
#define N 2500010 //2*250 0000=500 0000
bool pm[N];
void Prime() //素数打表
{
pm[1] = 1; //1不是素数
for (int i = 2; i <= N; i++)
{
if (pm[i] == 1)
continue; //跳过已经标记为不是素数的数
for (int j = 2; j * i <= N; j++) //枚举i的倍数
pm[i * j] = 1; //标记该数不是素数
}
}
int SemiPrime(int st, int ed)
{
int i, j, m, k = 0, t = sqrt(ed);
for (i = 2; i <= t; ++i)
{
if (pm[i])
continue; //寻找质数作为第1个乘数
for (j = i; j <= ed / 2; ++j)
{
if (!pm[j]) //第2个乘数也是素数
{
m = i * j; //两素数乘积
if (m < st)
continue; //比给定区间小则继续
else if (m > ed)
break; //比给定区间大则不必再乘后面的数
else
k++; //在区间内则计数
}
}
}
return k;
}
int main()
{
int S, E;
scanf("%d%d", &S, &E);
Prime();
printf("%d\n", SemiPrime(S, E));
return 0;
}
该博客讨论了如何高效地解决寻找指定范围内半质数的问题。通过使用筛法预先生成素数表,然后枚举两个素数作为半质数的乘积,优化算法以适应大数据量和内存限制。博主提供了算法细节,包括限制两个乘数的范围以及根据乘积调整循环条件,确保在给定的限制内找到所有半质数。
1984

被折叠的 条评论
为什么被折叠?



