信息学奥赛一本通 1619:【例 1】Prime Distance
题目要求求出区间[L, R]中相邻质数的最大差值和最小差值。由于R-L≤10^6,采用筛法先预处理[1,50000]中的质数,再用这些质数筛去[L,R]中的合数。剩下的质数存入数组后遍历求相邻差值即可。关键点包括: 使用线性筛预处理小质数 通过区间偏移映射大数到数组 处理边界情况如L=1和质数数量不足的情况 使用数学公式计算质数倍数的范围以提高效率 最终通过遍历筛选后的质数数组,即可得到所需的最大
【题目链接】
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} L、R<231,也就是会达到 1 0 9 10^9 109量级。
以 O ( n ) O(n) O(n)的线性筛求 1 ∼ 1 0 9 1\sim 10^9 1∼109中的所有质数,也会超时。
而本题具有特征 R − L ≤ 1 0 6 R-L\le 10^6 R−L≤106。因此可以考虑只对区间 [ 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.5∗109,那么 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 k⋅pi,那么 k ⋅ p i ≥ L k\cdot p_i \ge L k⋅pi≥L
所以 k ≥ L p i k\ge \frac{L}{p_i} k≥piL, 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 ⌈piL⌉pi
求小于等于 R R R的最大的 p i p_i pi的倍数,设该数为 k ⋅ p i k\cdot p_i k⋅pi,那么 k ⋅ p i ≤ R k\cdot p_i \le R k⋅pi≤R
所以 k ≤ R p i k\le \frac{R}{p_i} k≤piR, 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 ⌊piR⌋pi
设 j j j表示 p i p_i pi的倍数,要想使 L ≤ j ⋅ p i ≤ R L\le j\cdot p_i\le R L≤j⋅pi≤R,那么 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 j⋅pi筛掉。特殊地,当 j j j为1时,取到的值为 p i p_i pi,是质数 ,不用筛掉。
由于 j ⋅ p i j\cdot p_i j⋅pi的取值最大可以达到 1 0 9 10^9 109,无法作为数组下标,即无法直接设数组isPrime[i]表示i是否为质数。此处可以利用性质 R − L ≤ 1 0 6 R-L\le 10^6 R−L≤106,只表示该范围内的数是否为质数。设布尔型数组dip,数组长度为 1 0 6 10^6 106。dp[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 231−1=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;
}
更多推荐




所有评论(0)