题意
给你一个区间[L, R], R − L ≤ 1 0 6 R-L \le 10^6 R−L≤106 ,问这段区间内相邻质数距离最大和最小分别是哪些数。请输出这些数字,若没有则输出无解。
思路
最开始一眼相当的就是质数筛,但是一看数据范围 2 9 2^9 29果断放弃。其实思路也是相当于筛法。只是这里是筛合数。
对于本题有以下性质:
1.若n是合数,必然存在两个因子 d , n d d, \frac{n}{d} d,dn,并且 d ≤ n d \le \sqrt{n} d≤n
2.若 x ∈ [ L , R ] x \in [L, R] x∈[L,R]并且x是合数必然存在一个质因子 p ∈ [ 1 , 2 9 ] p \in[1, \sqrt{2^9}] p∈[1,29], (p|x , p < x)
所以我们的思路就是将 [ 1 , 2 9 ] [1, \sqrt{2^9}] [1,29]相当于将[1, 50000] 中的所有质数筛出来,然后将[L, R]中这些质数的所有的倍数筛掉,也就是说将其中的所有的合数筛出来。最后统计出所有的质数计算答案就是了。
2023.7.19 PS:
对于为什么我们可以通过把所有的质数的倍数筛掉就可以确定能把所有的合数筛掉呢,首先我们由整数分解定理得知,我们可以把每个数都能够分解成若干个质因数相乘的结果。那么对于一个合数N而言
N
=
p
1
a
1
∗
p
2
a
2
.
.
.
∗
p
n
a
n
N = p_1^{a_1}*p_2^{a_2}...*p_n^{a_n}
N=p1a1∗p2a2...∗pnan那么他一定是可以化成一下若干的式子。
N
=
p
1
∗
k
1
N
=
p
2
∗
k
2
N
=
p
3
∗
k
3
N = p_1*k_1 \\ N = p_2*k_2 \\ N = p_3*k_3 \\
N=p1∗k1N=p2∗k2N=p3∗k3
所以我们只要能够求出一定范围的质数,那么我们就可一通过把这些质数的倍数筛掉从而达到筛掉所有的质数。
那么对于此解法我们就涉及到时间复杂度的问题了。
筛质数: O(N);
筛合数. O(Nloglog(N))
对于筛合数的时间复杂度解释:
T = 1 0 6 2 + 1 0 6 3 + 1 0 6 5 + . . . + 1 0 6 N T = \frac{10^{6}}{2}+\frac{10^6}{3}+\frac{10^6}{5}+...+\frac{10^6}{\sqrt{N}} T=2106+3106+5106+...+N106
T = 1 0 6 ∗ ( 1 2 + 1 3 + 1 5 + . . . 1 ( N ) ) T=10^6*(\frac{1}{2}+\frac{1}{3}+\frac{1}{5}+...\frac{1}{\sqrt(N)}) T=106∗(21+31+51+...(N)1)
对于后面这堆求和的结果在埃氏筛法中已经得出结果为loglogN
综上所述我们的算法接近于一个O(N)的时间复杂度。
对于本题代码会有一些细节会在代码中详细给出。
代码
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr)
#define int long long
#define endl "\n"
#define xx first
#define yy second
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 1e9 + 7, M = 50000;
int n, m, k, _, cnt;
int pr[N];
bool st[N];
int arr[N];
void init(int x) //线性筛
{
for(int i = 2; i <= x; i ++)
{
if(!st[i]) pr[cnt++] = i;
for(int j = 0; pr[j]*i <= x; j ++)
{
st[i*pr[j]] = 1;
if(i % pr[j] == 0) break;
}
}
}
void solve()
{
int l, r;
while(cin >> l >> r)
{
cnt = 0;
init(50000);
memset(st, 0, sizeof st);
for(int i = 0; i < cnt; i ++)
{
int p = pr[i];
for(int j = max(2*p, (l+p-1)/p*p); j <= r; j += p)
{
st[j-l] = 1;
//由于数最大为2^9,数组是存不下的所以我们储存的是偏移量
}
//j = max(2*p, (l+p-1)/p*p) 我们要找出大于等于L的p的倍数然后把每一个P的倍数筛掉.
//为什么是2*p,首先我们要清楚我们筛的是合数,对于每一个P他是质数,所以最小的关于P的倍数且是合数的是2*P
}
cnt = 0;
for(int i = 0 ;i <= r-l; i ++)//因为储存的是偏移量
{
if(!st[i] && i+l >= 2) //i+l >= 2是因为1不是质数
{
arr[cnt++] = i+l;
//cout << i+l << " ";
}
}
//cout << endl;
if(cnt < 2)
{
cout << "There are no adjacent primes." << endl;
continue;
}
int minp = 0, maxp = 0;
for(int i = 0; i+1 < cnt; i ++)
{
int d = arr[i+1] - arr[i];
if(d < arr[minp+1] - arr[minp]) minp = i;
if(d > arr[maxp+1] - arr[maxp]) maxp = i;
}
cout << arr[minp] << "," << arr[minp+1] << " are closest, ";
cout << arr[maxp] << "," << arr[maxp+1] << " are most distant." << endl;
}
}
signed main()
{
IOS;
solve();
return 0;
}