POJ 3292 Semi-prime H-numbers
题目大意:
形如 4 n + 1 4n + 1 4n+1的数被称为 H H H数,乘法在“ H − H- H−数”内组成集合是封闭的,在这个集合中只能被 1 1 1和它本身整除的数叫“ H − H- H−素数“,其余的数叫做” H − H- H−合数“,在” H − H- H−合数“中还存在着” H − H- H−合成数“,” H − H- H−合成数“是一个只能分解成两个“ H − H- H−素数“乘积的” H − H- H−合数“(可能有多种分解方案),例如, 441 = 21 ∗ 21 = 9 ∗ 49 441 = 21 * 21 = 9 * 49 441=21∗21=9∗49,所以 441 441 441是” H − H- H−合成数“
求 0 ∼ h 0 \sim h 0∼h范围内的” H − H- H−合成数“的个数。
输入
若干行,每一行一个小于等于 1000001 1000001 1000001的整数 k k k,一个 0 0 0表示输入结束
输出
对于每一行输入,先输出这个数然后空格,再输出这个数对应的答案
样例输入
21
85
789
0
样例输出
21 0
85 5
789 62
思路很简单,首先将所有范围内的 H H H数中的 H H H素数全部找出来,因为 H H H数满足 4 n + 1 4n + 1 4n+1,因此可以从 5 5 5开始,每次加 4 4 4,然后找 H H H素数,可以通过埃氏筛法的思路来求,对于第一个出现的 H H H数,一定是 H H H素数,因此这个数的所有倍数一定不是 H H H素数,做好相应标记,同时打表记录下来。
找到所有的 H H H素数之后,就可以双重循环把所有的可能的情况乘一下,乘出来的结果,如果是 H H H数,那么一定是 H H H合成数。最后再范围内找个数,通过一个前缀和即可
代码
#include <iostream>
#define N 1000005
#define ll long long
using namespace std;
int semi_prime[N];
int h[N], h_prime[N];
int cnt;
void solve() {
for(int i = 5; i < N; i += 4) {
if(h[i]) continue;
h_prime[++cnt] = i; //存所有的H素数
for(int j = i * 2; j < N; j += i) //将i这个H素数的所有倍数筛掉
h[j] = 1;
}
for(int i = 1; i <= cnt; i++) {
if(h_prime[i] * h_prime[i] > N) break; //剪枝,避免超时
for(int j = i; j <= cnt; j++) {
ll k = h_prime[i] * h_prime[j]; //剪枝
if(k > N) break;
if(k % 4 == 1) //如果两个H素数之积是H数,那么一定是H合成数,记录相应位置
semi_prime[k] = 1;
}
}
for(int i = 1; i < N-1; i++) //前缀和
semi_prime[i] += semi_prime[i-1];
}
int main () {
ios::sync_with_stdio(0);
int h;
solve();
cin >> h;
while(h) {
cout << h << " " << semi_prime[h] << "\n";
cin >> h;
}
return 0;
}