//5248K 16MS G++
#include <stdio.h>
#include <string.h>
const int MAX = 1000010;
char HPNumber[MAX];
int HSPNumber[MAX];
void table() {
memset(HPNumber, 0 ,sizeof(HPNumber));
memset(HPNumber, 0 ,sizeof(HSPNumber));
// at begin, all H-number are h-prime
for (int i = 5; i <= MAX; i = i+4) {
for (int j = 5; j <= MAX; j = j+4) {
int multiply = i*j;
if (multiply > MAX) {
break;
}
// i and j are all h-prime, semi-h-prime
if (!HPNumber[i] && !HPNumber[j]) {
HPNumber[multiply] = -1;
} else { // i and j all not all semi-h-prime
HPNumber[multiply] = -2; // not semi-h-prime
}
}
}
int curHSPNum = 0;
for (int i = 1; i <= MAX; i ++) {
if (HPNumber[i] == -1) { // counter a h-semi-prime
curHSPNum++;
}
HSPNumber[i] = curHSPNum;
}
}
int N;
int main() {
table();
while(scanf("%d", &N) != EOF) {
if (!N) {
return 0;
}
printf("%d %d\n", N, HSPNumber[N]);
}
}
从解法上看算是个水题,就是纯遍历进行筛选,然后统计一下N之前有多少个H-semi-prime,
不过还是有重要思路的,首先,如何确定一个H-prime? 我最开始还向着是不是先搞一遍素数筛子,然后,再结合H-number来得到 H-prime,
后来,看了别人的题解,才发觉自己是想多了,完全可以假设一开始所有的h-number都是h-prime, 然后再从小到大的进行乘积筛选,逐渐去掉非h-prime,
同时根据乘积来判断 h-semi-number 和 非h-semi-number,
流程就是:
两重循环h-number,都是从5开始(最小的非1 h-number)这一点很关键,保证了算法的正确性,分别用i, j表示,
而 i * j 也必定是h-numner((4*A + 1)*(4*B + 1) 最后也可以表示为 4*C + 1 的形式), 如果 i 和 j 都是 h-prime, 那么 i*j 就是 h-semi-prime,在HP[]数组中将i 和 j置为 -1(初始为0,表示为h-prime)
而如果i 和 j 不全是h-prime, 那么i * j必定不是 h-semi-prime,因为必然可以表示为3个或以上的h-prime相乘, 置为-2(为何和 h-semi-prime 和 h-prime区分开)
最后遍历整个HP[]数组,统计一下每个数前面出现了多少个h-semi-prime即可。
这个题的思路本质上和素数筛子差不多,是不过多了一种标示罢了,都是先假设全部的数都是满足条件的,然后从小到大,一一排除.