蓝书(算法竞赛进阶指南)刷题记录——POJ2689 Prime Distance(筛法)

题目:POJ2689.
题目大意:给定区间 [ L , R ] [L,R] [L,R],要求区间中差最小的相邻素数和差最大的相邻素数.
1 ≤ L &lt; R ≤ 2 31 − 1 , 1 ≤ R − L + 1 ≤ 1 0 6 1\leq L&lt;R\leq 2^{31}-1,1\leq R-L+1\leq 10^6 1L<R2311,1RL+1106.

这道题一看不可做,毕竟 L , R &lt; 2 31 L,R&lt; 2^{31} L,R<231的数据范围太大,但是一看 R − L ≤ 1 0 6 R-L\leq 10^6 RL106的限制就会想到一定是从区间 [ L , R ] [L,R] [L,R]中的数入手.

想到尝试枚举区间 [ L , R ] [L,R] [L,R]中的每一个数,暴力计算每一个数是否为合数,然后统计,发现这样做时间复杂度是 O ( ( R − L ) R ) O((R-L)\sqrt R) O((RL)R )的,过不了.

考虑一个数为合数时因数的性质,发现一个合数 n n n必定有一个因数 x x x满足 x ≤ n x\leq \sqrt n xn ,所以考虑利用这个性质来做这道题.

利用上面那个性质可以想到,区间 [ L , R ] [L,R] [L,R]中的任意一个合数必定有一个因数 x x x满足 x ≤ R x\leq \sqrt R xR ,所以可以枚举 [ 2 , R ] [2,\sqrt R] [2,R ]内的所有数,把这些数在区间 [ L , R ] [L,R] [L,R]内的倍数全部打上合数的标记,最后把没打标记的数取出来统计即可.时间复杂度 O ( ( R − L ) log ⁡ R ) O((R-L)\log\sqrt R) O((RL)logR ),但是我用这个做法TLE了…

考虑优化,每次把 [ 2 , R ] [2,\sqrt R] [2,R ]内的质数筛出来,直接用质数去筛 [ L , R ] [L,R] [L,R]内的数即可.按照 [ 1 , n ] [1,n] [1,n]中的素数有 O ( n log ⁡ n ) O(\frac{n}{\log n}) O(lognn)来计算的话,这个算法的时间复杂度约为 O ( R + ( R − L ) log ⁡ log ⁡ R ) O(\sqrt R+(R-L)\log\log\sqrt{R}) O(R +(RL)loglogR ).

代码如下:

#include<cstdio>
#include<iostream>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=1000000;

int pr[N+9],tpr,b[N+9];
int l,r;
int cnt[N+9],p[N+9],tp;
int mx,mn;

Abigail sieve(int n){
  for (int i=2;i<=n;++i) b[i]=1;
  for (int i=2;i<=n;++i){
  	if (b[i]) pr[++tpr]=i;
  	for (int j=1;j<=tpr&&i*pr[j]<=n;++j){
  	  b[i*pr[j]]=0;
  	  if (i%pr[j]==0) break;
  	}
  }
} 

Abigail work(){
  int ll,rr,now=0;
  for (now=1;now<=tpr&&pr[now]*pr[now]<=r;++now);
  --now;
  for (int i=1;i<=tpr;++i){
  	ll=l/pr[i];
    if (pr[i]*ll<l) ++ll;
    if (ll<2) ll=2;      //避免自己把自己筛掉 
    rr=r/pr[i];
    for (int j=ll;j<=rr;++j)
      cnt[pr[i]*j-l+1]=1;
  }
  for (int i=0;i<=r-l;++i)
    if (!cnt[i+1]&&l+i>1) p[++tp]=l+i;
  mx=mn=1;
  for (int i=2;i<tp;++i){
    if (p[mx+1]-p[mx]<p[i+1]-p[i]) mx=i;
    if (p[mn+1]-p[mn]>p[i+1]-p[i]) mn=i;
  } 
}

Abigail outo(){
  if (tp<2) puts("There are no adjacent primes.");
  else printf("%d,%d are closest, %d,%d are most distant.\n",p[mn],p[mn+1],p[mx],p[mx+1]);
  for (int i=1;i<=r-l+1;++i)
    cnt[i]=0;
  tp=0;
}

int main(){
  sieve(N);
  while (~scanf("%d%d",&l,&r)){
    work();
    outo();
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值