【题目链接】

ybt 1619:【例 1】Prime Distance
LOJ 10197. 「一本通 6.2 例 1」Prime Distance

【题目考点】

1. 筛法求质数:埃筛、欧拉筛

相关知识见:
ybt 2040:【例5.7】筛选法找质数

【解题思路】

直观的思路是:将区间 [ L , R ] [L, R] [L,R]中所有质数添加进顺序表v中,遍历顺序表即可求出相邻质数差值最大的数对和差值最小的数对。

而筛法只能求1~n中的所有质数,本题 L 、 R < 2 31 L、R<2^{31} LR<231,也就是会达到 1 0 9 10^9 109量级。
O ( n ) O(n) O(n)的线性筛求 1 ∼ 1 0 9 1\sim 10^9 1109中的所有质数,也会超时。

而本题具有特征 R − L ≤ 1 0 6 R-L\le 10^6 RL106。因此可以考虑只对区间 [ L , R ] [L, R] [L,R]进行筛法求质数,筛掉其中的合数,剩下的就是质数。

根据对埃筛的认识,要想筛掉区间 [ 1 , n ] [1,n] [1,n]中的所有合数,需要筛掉 [ 2 , n ] [2, \sqrt{n}] [2,n ]中所有质数的倍数(2倍及以上)。
因此要想筛掉区间 [ L , R ] [L, R] [L,R]中的合数,需要筛掉 [ 2 , R ] [2, \sqrt{R}] [2,R ]中所有质数的倍数。
R < 2 31 < 2.5 ∗ 1 0 9 R<2^{31}<2.5*10^9 R<231<2.5109,那么 R ≤ 50000 \sqrt{R} \le 50000 R 50000
所以只需要先求出区间 [ 1 , 50000 ] [1, 50000] [1,50000]中所有的质数。而后对区间 [ L , R ] [L, R] [L,R]筛掉所有区间 [ 1 , 50000 ] [1, 50000] [1,50000]中质数的倍数,剩下的就是区间 [ L , R ] [L, R] [L,R]中的质数。

首先通过埃筛或线性筛求出区间 [ 1 , 50000 ] [1, 50000] [1,50000]中所有的质数,
枚举其中的每个质数,对于其中第i个质数记为 p i p_i pi,筛掉区间 [ L , R ] [L, R] [L,R] p i p_i pi的倍数。

求大于等于 L L L的最小的 p i p_i pi的倍数,设该数为 k ⋅ p i k\cdot p_i kpi,那么 k ⋅ p i ≥ L k\cdot p_i \ge L kpiL
所以 k ≥ L p i k\ge \frac{L}{p_i} kpiL k k k的最小取值为 ⌈ L p i ⌉ \lceil \frac{L}{p_i} \rceil piL
因此大于等于 L L L的最小的 p i p_i pi的倍数为 ⌈ L p i ⌉ p i \lceil \frac{L}{p_i} \rceil p_i piLpi
求小于等于 R R R的最大的 p i p_i pi的倍数,设该数为 k ⋅ p i k\cdot p_i kpi,那么 k ⋅ p i ≤ R k\cdot p_i \le R kpiR
所以 k ≤ R p i k\le \frac{R}{p_i} kpiR k k k的最大取值为 ⌊ R p i ⌋ \lfloor \frac{R}{p_i} \rfloor piR
因此,小于等于 R R R的最大的 p i p_i pi的倍数为 ⌊ R p i ⌋ p i \lfloor \frac{R}{p_i} \rfloor p_i piRpi

j j j表示 p i p_i pi的倍数,要想使 L ≤ j ⋅ p i ≤ R L\le j\cdot p_i\le R LjpiR,那么 j ∈ [ ⌈ L p i ⌉ , ⌊ R p i ⌋ ] j\in [\lceil \frac{L}{p_i} \rceil, \lfloor \frac{R}{p_i} \rfloor] j[⌈piL,piR⌋]。枚举 [ ⌈ L p i ⌉ , ⌊ R p i ⌋ ] [\lceil \frac{L}{p_i} \rceil, \lfloor \frac{R}{p_i} \rfloor] [⌈piL,piR⌋]范围内所有 j j j的值,将 j ⋅ p i j\cdot p_i jpi筛掉。特殊地,当 j j j为1时,取到的值为 p i p_i pi,是质数 ,不用筛掉。

由于 j ⋅ p i j\cdot p_i jpi的取值最大可以达到 1 0 9 10^9 109,无法作为数组下标,即无法直接设数组isPrime[i]表示i是否为质数。此处可以利用性质 R − L ≤ 1 0 6 R-L\le 10^6 RL106,只表示该范围内的数是否为质数。设布尔型数组dip,数组长度为 1 0 6 10^6 106dp[i]表示 L + i L+i L+i是否为质数,或者可以描述为dip[i-L]表示 i i i是否为质数。
同时,如果 L L L为1,则应该特殊地设置1不是质数,即dip[1-1]=dip[0]=false

筛掉区间 [ L , R ] [L, R] [L,R]中所有质数的倍数后,遍历 [ L , R ] [L, R] [L,R]区间,将其中的质数取出,添加到一个顺序表(vector)中。如果顺序表长度小于2,则输出“没有质数对”。否则后遍历顺序表,求出相邻质数差值最大的数对和差值最小的数对。

注意:L、R的取值可能达到 2 31 − 1 = 2147483647 2^{31}-1=2147483647 2311=2147483647,部分运算的结果可能会超过int类型的范围,应该设为long long类型。

【题解代码】

#include<bits/stdc++.h>
using namespace std;
#define N 50005
bool isPrime[N], dip[1000005];//dip[i]:i+l是否是质数 
int prime[N], pn, l, r;
void initPrime(int n)//线性筛1~n中的质数 
{
	memset(isPrime, 1, sizeof(isPrime));
	for(int i = 2; i <= n; ++i)
	{
		if(isPrime[i])
			prime[++pn] = i;
		for(int j = 1; j <= pn && i*prime[j] <= n; ++j)
		{
			isPrime[i*prime[j]] = false;
			if(i%prime[j] == 0)
				break;
		}
	}
}
long long divCeil(long long a, long long b)
{
	return (a+b-1)/b;
}
bool getIsPrime(long long i)//判断数值i是否是质数 
{
	return dip[i-l];
}
void setIsPrime(long long i, bool val)//设置数值i是否质数 
{
	dip[i-l] = val;
}
int main()
{
	initPrime(50000);
	while(cin >> l >> r)
	{
		memset(dip, 1, sizeof(dip));
		for(int i = 1; i <= pn; ++i)
			for(int j = divCeil(l, prime[i]); j <= r/prime[i]; ++j) if(j > 1)
				setIsPrime((long long)j*prime[i], false);
		if(l == 1)
			setIsPrime(1, false);
		vector<int> v;
		for(long long i = l; i <= r; ++i) if(getIsPrime(i))
			v.push_back(i);
		if(v.size() < 2) 
			cout << "There are no adjacent primes." << endl;
		else
		{
			int mnl, mxl, mnv = 2e9, mxv = 0;
			for(int i = 0; i <= v.size()-2; ++i)
			{
				if(v[i+1]-v[i] > mxv)
					mxv = v[i+1]-v[i], mxl = i;
				if(v[i+1]-v[i] < mnv)
					mnv = v[i+1]-v[i], mnl = i;
			}
			cout << v[mnl] << ',' << v[mnl+1] << " are closest, " 
			     << v[mxl] << ',' << v[mxl+1] << " are most distant." << endl;
		}
	} 
	return 0;
}
Logo

电影级数字人,免显卡端渲染SDK,十行代码即可调用,工业级demo免费开源下载!

更多推荐