题意:题目比较容易混淆,要搞清楚一点,这里面所有的定义都是在4×k+1(k>=0)这个封闭的集合而言的,不要跟我们常用的自然数集混淆。

  题目要求我们计算 H-semi-primes, H-semi-primes是 两个H-primes的乘积, H-primes的定义为:在这个集合中只能由1和它本来相乘得来,并且1不是 H-primes;

  分析:这个题目我一开始是想打表记录一下的,但是没有筛法的效率,数据量过大,程序崩溃了(连超时的机会都不给我),看了多个别人的做法才知道,这个题目考查的是对于素数筛法,我们需要对素数筛法有一些变形。和正常筛法是不太一样的,这个有三种数,H-semi-primes,H-primes, H-composite,这三种数我们需要给他们标上不同的号,而且必须是不同的号,否则筛法会出错误,最后题目问的是(1,h)之间有多少个,我们应该使用一个数组记录这个答案,以便打表以后在o(1)的复杂度就找到答案。

  注意:可能有人感觉会有漏解,其实不会,有人可能会担心这一点,类似这样,5×25怎么办? 但是25的标记一定不是0,25在5×5的时候就被标记了1,我们在后面能遇到的能被两个数乘积表达的数,在以前的时候就肯定已经被表达出来,并且标记过了。

  感悟:感觉素数筛法好神奇,它的变形好巧妙,好精美。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 1000001
int prime[maxn+];
int ans[maxn+];
void make(){
memset(prime,,sizeof(prime));
///H—prime 标记为 0
for(int i = ;i <= maxn;i += ){
for(int j = ;j <= maxn;j += ){
if(i*j > maxn) break;
if(prime[i]== && prime[j]==)///这个判断0的充要条件就是题目中给的要求,只能是两个H-prime
///任意一个为1或者2都不可以
prime[i*j] = ;///H-semi-primes标记为1;
else prime[i*j] = ;/// H-composites必须为2或者其他
}
}
}
void get_ans(){
int tot = ;
for(int i = ;i <= maxn;i++){
if(prime[i] == ) tot++;///don't forget prime[i] shoule only be 1;
ans[i] = tot;
}
}
int main(){
int h;
make();
get_ans();
while(~scanf("%d",&h)){
if(!h) break;
printf("%d %d\n",h,ans[h]);
}
return ;
}
04-14 00:06