POJ 3292 Semi-prime H-numbers (素数筛法变形)

  题意:题目比较容易混淆,要搞清楚一点,这里面所有的定义都是在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+10];
int ans[maxn+10];
void make(){
    memset(prime,0,sizeof(prime));
    ///H—prime 标记为 0
    for(int i = 5;i <= maxn;i += 4){
        for(int j = 5;j <= maxn;j += 4){
            if(i*j > maxn) break;
            if(prime[i]==0 && prime[j]==0)///这个判断0的充要条件就是题目中给的要求,只能是两个H-prime
                ///任意一个为1或者2都不可以
                prime[i*j] = 1;///H-semi-primes标记为1;
            else prime[i*j] = 2;/// H-composites必须为2或者其他
        }
    }
}
void get_ans(){
    int tot = 0;
    for(int i = 1;i <= maxn;i++){
        if(prime[i] == 1) 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
",h,ans[h]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/jifahu/p/5580298.html