【POJ 2689】Prime Distance【数论,数学】

题目大意:

题目链接:http://poj.org/problem?id=2689
l l l r r r之间相邻的差最小以及最大的质数。


思路:

这道题真的是烦。。。
这里写图片描述
这里写图片描述
MLE和RE了超级多次,最后发现是一个极其不起眼的东西。。。
这道题 l , r ≤ 2 31 l,r\leq 2^{31} l,r231,但是 r − l ≤ 1 0 6 r-l\leq 10^6 rl106,所以可以考虑从 l , r l,r l,r方面入手。
首先我们知道 n n n的质因子枚举到 n \sqrt{n} n 就可以了,那我们可以先将不大于 2 31 2^{31} 231次方的质数用线性筛筛出来,时间复杂度 O ( r ) O(\sqrt{r}) O(r ),约为 O ( 47000 ) O(47000) O(47000)
然后对于每一组数据,我们先从枚举每个质数,再从 l l l r r r枚举,每次直接加 p r i m e [ i ] prime[i] prime[i],然后被枚举到的数就绝对是合数。这里的理论时间复杂度是 O ( ( r − l ) r ) O((r-l)\sqrt{r}) O((rl)r ),语约为 O ( 47000000000 ) O(47000000000) O(47000000000),完全会 T T T。但是这只是理论,实际来说,当 r = 2 31 r=2^{31} r=231时,所有枚举的质数约为 5000 5000 5000个,第二重循坏最多执行 500000 500000 500000次,最少执行 1 1 1次,平均约为 10 10 10次!(经程序输出)
那么这一段的时间复杂度就约为 O ( 50000 ) O(50000) O(50000),但是还是有一些卡时。
当然你还可以用 g o t o goto goto再简化。在POJ上评测加了 g o t o goto goto会少 20 m s 20ms 20ms(当然并不建议用 g o t o goto goto)。
之后我们就求出了 l l l r r r之间的所有质数,那么接下来暴力枚举就可以啦!


代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#define ll long long
#define M 1000020
#define Inf 1e16
using namespace std;

ll l,r,m,sum,minn,maxn,prime[M],v[M],tail,max_r,max_l,min_r,min_l;
bool ok,p[M];

void find_prime(ll n)  //离线求质数(线性筛)
{
	for (ll i=2;i<=n;i++)
	{
		if (!v[i])
		{
			prime[++m]=i;
			v[i]=i;
		}
		for (ll j=1;j<=m;j++)
		{
			if (prime[j]>v[i]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
		}
	}
}

int main()
{
	find_prime(50000);
	while (~scanf("%lld%lld",&l,&r))
	{
		sum=r-l+1;
		memset(p,0,sizeof(p));
		if (l==1)   //特判,1不是质数
		{
			p[1]=1;
			sum--;
		}
		for (ll i=1;prime[i]<=sqrt(r);i++)
		 for (ll j=l+(prime[i]-(l%prime[i]))%prime[i];j<=r;j+=prime[i])  //直接求出第一个质数,然后就每次加prime[i]
		 {
		 	if (sum<2) goto stop;  //不建议使用
		 	if (j<0||prime[i]==j) continue;
		 	if (!p[j-l+1]) sum--;
		 	p[j-l+1]=true;
		 } 
		stop:
		if (sum<2) 
		{
			printf("There are no adjacent primes.\n");
			continue;
		}
		minn=Inf;
		maxn=-Inf;
		tail=-1;
		for (ll i=l;i<=r;i++)  //爆枚
		 if (!p[i-l+1])
		 {
		 	if (tail>-1)
		 	{
		 		if (i-tail>maxn)
		 		{
		 			maxn=i-tail;
		 			max_l=tail;
		 			max_r=i;
		 		}
		 		if (i-tail<minn)
		 		{
		 			minn=i-tail;
		 			min_l=tail;
		 			min_r=i;
		 		}
		 	}
		 	tail=i;
		 }
		printf("%lld,",min_l);
		printf("%lld are closest, ",min_r);
		printf("%lld,",max_l);
		printf("%lld are most distant.\n",max_r);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值