质数距离 acwing

196. 质数距离 - AcWing题库

题意是给你一个区间[l,r],相当于让你把这个区间里面所有的质数提出来,然后找到相邻质数距离最近和最远的第一个点在什么地方

看一下数据范围,不同的数据范围决定了用什么方法去做:

 两个数据范围:一个是区间[l,r]本身是2^31-1,还有区间的长度是1e6

分析:

2^31-1约等于2e9。如果范围是1e7这样是可以把所有的质数筛出来然后扫描一遍的。1e9不可行

接下来使用的方法是二次筛法:

总共是2e9的范围。如果是一个合数,他的因数取前一半是<=sqrt(合数)。反过来说,如果一个数它的前根号的范围内没有它的因子,那后半部分也没有它的因子。如果是这样的话,可以推出它不是一个合数,而是一个质数。

所以取到极限在2e9附近,如果是合数,50000之内一定会有他们的因子,如果没有那就是质数。

因为任何一个数都可以分解质因数。我们使用一下埃氏筛的原理:先把50000里面所有的质数筛出来,然后用每个质数把区间[l,r]里面的合数全部筛掉,就可以得到区间里面的质数了,然后最后扫描一遍。

筛:使用质数的倍数去筛,首先找到大于等于l小于等于r的最小的p的倍数(p是质因子)

答案是\left \lceil \frac{L}{P} \right \rceil*P,转化一下是\left \lfloor \frac{L+P-1}{P} \right \rfloor*P

(这里有一个很基础的板子,就是问最大的不超过p的x的倍数是多少,然后稍稍转化一下)

然后下面是很重要的筛区间里面的合数了:因为数很大,不可以直接用数组的索引来存。所以我们把每次得到的合数映射到这段区间里面此时所在的下标然后标记一下,即st[j-l],就是得到的数减去边界l。

在区间里面的合数全部筛完之后,我们开始遍历这个区间,如果遍历到的下标被标记,也就是此所在位置是个合数,然后把它恢复到这个下标所对应的质数上,用质数筛去存。即边界l加上当前的索引下标:i+l(具体的字母含义见下面代码)

然后操作完就得到了区间里面所有的质数,然后扫描一遍即可出答案

总结:

这个就是二次筛法,先把50000里面所有的质数筛出来,然后利用这些质数把所想要的区间里面的合数筛掉,留下质数,接着操作。这样筛用的方法是数组的下标映射。

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define IOS ios::sync_with_stdio(false), cin.tie(0);
#include<iostream>
#include<map>
#include<set> 
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
#include<cmath>
#include<queue>
#include<deque>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PAII;
const int N=2e6+10,M=5050,INF=1e18,mod=1e9+7,NN=10000010;
int cnt;
bool st[N];
int prime[N];
void init()
{
	int n=50000;
	memset(st,0,sizeof(st));
	for(int i=2;i<=n;i++)
	{
		if(!st[i]) prime[cnt++]=i;
		for(int j=0;prime[j]<=n/i;j++)
		{
			st[prime[j]*i]=true;
			if(i%prime[j]==0) break;
		}
	}
}
signed main(){
    //IOS;
    int T;
    T=1;
    //cin>>T;
    while(T--)
    {
    	int l,r;
    	while(cin>>l>>r)
    	{
    		cnt=0;
			init();
			memset(st,0,sizeof(st));
			for(int i=0;i<cnt;i++)
			{
				int p=prime[i];
				for(int j=max(p*2,(l+p-1)/p*p);j<=r;j+=p)
					st[j-l]=true;
			}
			cnt=0;
			for(int i=0;i<=r-l;i++)
				if(!st[i]&&i+l>=2)
					prime[cnt++]=l+i;
			int maxn=0,minn=0;
			for(int i=0;i+1<cnt;i++)
			{
				int d=prime[i+1]-prime[i];
				if(d<prime[minn+1]-prime[minn]) minn=i;
				if(d>prime[maxn+1]-prime[maxn]) maxn=i;
			}
			if(cnt<2) cout<<"There are no adjacent primes.\n";
			else printf("%lld,%lld are closest, %lld,%lld are most distant.\n",prime[minn],prime[minn+1],prime[maxn],prime[maxn+1]);

			
		}
	}
    return 0;
} 
/*
1 3 7 9
2 4 6 8 5
5 4
a<b



*/ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值