POJ_3292_Semi-prime H number_筛法

今天下午数据结构大作业pre相当成功啊

题意

定义H number为形如4*i+1的正整数,i为任意非负整数,注意H数乘法封闭。
定义H prime number 为除了1和它本身,无法被其他H数整除的数字,1不是prime number。
定义H-semi-prime number为由两个H prime number相乘得到的H数。
给出一个数字,问小于等于这个数字的H-semi-prime number有多少个。

输入输出

Input

Each line of input contains an H-number ≤ 1,000,001. The last line of input contains 0 and this line should not be processed.

Output

For each inputted H-number h, print a line stating h and the number of H-semi-primes between 1 and h inclusive, separated by one space in the format shown in the sample.

分析

由于一开始对筛法的理解不够,加上一开始代码写挫了,我一直觉得不能跑两遍筛法(第二遍为类筛法),但其实是可以的。
实际上,筛法的时间开销小,不主要是因为它删除了很多点,而是因为第二重循环它是跳着跑的。
我的解法是先跑一边筛法,筛素数,然后二重循环遍历所有的素数,将其乘积设为semi-prime,只要加上两数相乘小于最大值的判断,就可以了,事实上第二遍素数的计算次数应当比第一次筛法小。一开始没加两数相乘小于最大值,结果跑半天才跑出来。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define mxn 1000010
int n,cnt;
int semi[mxn];
int is_hp[mxn];
int p[mxn];
void set(){
    cnt=0;
    memset(is_hp,0,sizeof(is_hp));
    for(int i=1;4*i+1<mxn;++i)  if(is_hp[4*i+1]==0)
        for(int j=i;(long long)(4*i+1)*(4*j+1)<mxn;++j){
            int tem=(4*i+1)*(4*j+1);
            is_hp[tem]=1;
        }
    int cur=0;
    for(int i=1;4*i+1<mxn;++i)  if(is_hp[4*i+1]==0)
        p[cur++]=4*i+1;
    for(int i=0;i<cur;++i)
        for(int j=i;j<cur&&(long long)p[i]*p[j]<mxn;++j)
            semi[cnt++]=p[i]*p[j];
    sort(semi,semi+cnt);
    cnt=unique(semi,semi+cnt)-semi;
}
int bin(int l,int r){
    int m;
    while(l+1<r){
        m=(l+r)>>1;
        if(semi[m]<=n)  l=m;
        else    r=m;
    }
    if(semi[l]<=n)  return l+1;
    return 0;
}
int main(){
    set();
    while(scanf("%d",&n)!=EOF){
        if(!n)  break;
        int ans=bin(0,cnt);
        printf("%d %d\n",n,ans);
    }
    return 0;
}
点赞