区间筛+例题(POJ2689)

区间筛
  区间筛一般用于那种超大区间的素数筛选当中,例如我给你一个区间 [ l , r ],其中 l < r && r<=2 ^31, r - l<=10 ^ 6,这个时候我们已知的筛法不可能从2 ~ r去筛选,同时也不能从 l ~ r 去筛选(无法开这么大的数组,会爆)。那我们这个时候能够用什么去筛选呢?我们应该不难想到 r 中所有的合数的最小质因子一定是小于等于根号 r,那么我们可以先筛选 2 ~ 根号r 中的所有素数,然后去标记 l ~ r中所含的能够被整除2 ~ 根号r 之间素数整除的数,再去跑一遍 l ~ r 那些没有被标记的数,就是素数。那么如何用 2 ~ 根号r 之间的素数去标记 l ~ r 中其能整除的数呢?我们不难想到有埃式筛去做。
  埃式筛的代码如下:

代码:
void primes(int n){
    memset(v,0,sizeof v);  //合数标记
    for(int i=2;i<=n;i++){
        if(v[i])continue; 
        cout<<i<<endl; //i是质数
        for(int j=i;j<=n/i;j++)v[i*j]=1;
    }
}

  我们只需要将在埃式筛的后面多加一个 j 从( l + i -1)/ i 到 r / i 的循环,将 l ~ r 变为 0 ~ r - l ,然后将埃式筛稍微改一下。

  代码如下:

const int maxn=1e6+5;
bool is_prime[maxn]; //标记数组,用来标记 l 到 r 之间的合数
bool is_prime_small[maxn];  //标记数组,用来标记 2~根号r之间的合数
void segment_sieve(LL l,LL r){
	for(LL i=0;i*i<=r;i++)is_prime_small[i]=true;   
	for(LL i=0;i<=r-l;i++)is_prime[i]=true;         
	for(LL i=2;i*i<=r;i++){        
		if(is_prime_small[i]){          
			for(LL j=2*i;j*j<=r;j+=i)is_prime_small[j]=false;   //标记2 ~ 根号r之间的合数 
			for(LL j=max((LL)2,(l+i-1)/i)*i;j<=a;j+=i)is_prime[j-l]=false;//标记2到r之间的合数 
		}
	}
}

这里要注意一下 0和1需要特判一下。

接下来是一道例题:
POj 2689
Prime Distance

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 31932 Accepted: 8120

Description

The branch of mathematics called number theory is about properties of numbers. One of the areas that has captured the interest of number theoreticians for thousands of years is the question of primality. A prime number is a number that is has no proper factors (it is only evenly divisible by 1 and itself). The first prime numbers are 2,3,5,7 but they quickly become less frequent. One of the interesting questions is how dense they are in various ranges. Adjacent primes are two numbers that are both primes, but there are no other prime numbers between the adjacent primes. For example, 2,3 are the only adjacent primes that are also adjacent numbers.
Your program is given 2 numbers: L and U (1<=L< U<=2,147,483,647), and you are to find the two adjacent primes C1 and C2 (L<=C1< C2<=U) that are closest (i.e. C2-C1 is the minimum). If there are other pairs that are the same distance apart, use the first pair. You are also to find the two adjacent primes D1 and D2 (L<=D1< D2<=U) where D1 and D2 are as distant from each other as possible (again choosing the first pair if there is a tie).

Input

Each line of input will contain two positive integers, L and U, with L < U. The difference between L and U will not exceed 1,000,000.

Output

For each L and U, the output will either be the statement that there are no adjacent primes (because there are less than two primes between the two given numbers) or a line giving the two pairs of adjacent primes.

Sample Input

2 17
14 17

Sample Output

2,3 are closest, 7,11 are most distant.
There are no adjacent primes.

提交 submit

  这道题的大致意思就是给你一个区间[ l , r ],让你去算这个区间中两个相邻素数之间差的最大值与最小值。
  思路:这道题题目所给的 l 与 r都是小于2 ^ 31,同时 r - l <= 10 ^ 6 那么根据这个数据范围,我们不难想到是用区间筛去做的。我们先用区间筛去标记 l ~ r 之间的合数,然后再去遍历一遍 [ l , r ], 将未标记的数(即素数)存放到prime数组中去,统计一下 l ~ r之间的素数个数,如果个数小于2的话,直接输出“There are no adjacent primes.”,反之,则走一遍prime数组,去算最大与最小值。(注意:1需要特判,1不是素数)。
   具体代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
bool is_prime[maxn];
bool is_prime_small[maxn];
LL prime[maxn]; 
LL ans=0;
void segment_sieve(LL a,LL b){
	for(LL i=0;i*i<=b;i++)is_prime_small[i]=true;  //标记2~根号b之间的和数 
	for(LL i=0;i<=b-a;i++)is_prime[i]=true;        //用来标记a到b之间的和数 
	for(LL i=2;i*i<=b;i++){        
		if(is_prime_small[i]){          
			for(LL j=2*i;j*j<=b;j+=i)is_prime_small[j]=false;
			for(LL j=max((LL)2,(a+i-1)/i)*i;j<=b;j+=i)is_prime[j-a]=false;//筛选a到b之间的素数 
		}
	}
}
int main(){
	LL a,b;
    LL r1,r2,l1,l2;
    int max1;int min1;
	while(~scanf("%lld%lld",&a,&b))
	{
        ans =0;   //统计素数个数
        max1=-1;min1=maxn;
        if(a==1&&b==1)printf("There are no adjacent primes.\n"); 
        else {
            if(a==1)a=2;
            segment_sieve(a,b);
	        for(LL i=0;i<=b-a;i++){
		        if(is_prime[i]){
			        prime[++ans]=i+a; //将[l,r]之间的素数存放到prime数组中
		        }
	        }
	        if(ans<2)printf("There are no adjacent primes.\n");//不符合,直接输出
            else
	       { 
	           for(int i=1;i<ans;i++){
               if(prime[i+1]-prime[i]>max1){
                   max1=prime[i+1]-prime[i];
                   l2=prime[i];r2=prime[i+1];
               } 
               if(prime[i+1]-prime[i]<min1){
                   min1=prime[i+1]-prime[i];
                   l1=prime[i];r1=prime[i+1];
               }
            }
            
            printf("%lld,%lld are closest, %lld,%lld are most distant.\n",l1,r1,l2,r2);}
        }
    }
}

道阻且长
自己选的路 跪着也要走完。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值