AcWing 196. 质数距离

文章目录

题意

给你一个区间[L, R], R − L ≤ 1 0 6 R-L \le 10^6 RL106 ,问这段区间内相邻质数距离最大和最小分别是哪些数。请输出这些数字,若没有则输出无解。

思路

最开始一眼相当的就是质数筛,但是一看数据范围 2 9 2^9 29果断放弃。其实思路也是相当于筛法。只是这里是筛合数

对于本题有以下性质:

1.若n是合数,必然存在两个因子 d , n d d, \frac{n}{d} d,dn,并且 d ≤ n d \le \sqrt{n} dn
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=p1a1p2a2...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=p1k1N=p2k2N=p3k3
所以我们只要能够求出一定范围的质数,那么我们就可一通过把这些质数的倍数筛掉从而达到筛掉所有的质数。

那么对于此解法我们就涉及到时间复杂度的问题了。

筛质数: 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+...+N 106
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值