AcWing
  • 首页
  • 活动
  • 题库
  • 竞赛
  • 应用
  • 更多
    • 题解
    • 分享
    • 商店
    • 问答
    • 吐槽
  • App
  • 登录/注册

AcWing 196. 质数距离    原题链接    中等

作者: 作者的头像   卡冈图亚 ,  2023-03-19 17:30:42 ,  所有人可见 ,  阅读 30


0


快速筛一个区间的质数(l或r很大但区间长度比较小)

受以前做过一道题的启发,其实筛质数的同时也是筛合数

判断一个数是否为合数,就要判断这个数是否有除1和自身以外的因子,换句话说,判断该数是否为除1和自身以外的数的倍数

为什么要换句话呢?因为传统的判断一个数是否为合数(质数)的方法是从2到√n枚举每个数,然后用取余是否为0的方法来加以区别,如果一个数为质数,就会把2到√n整个区间扫一遍,也就是√n的复杂度

但是对于一个区间来说,判断每个数都是√n(最坏情况),区间长度为m,那么总共就是m√n,这显然是不太友好的

既然筛质数会到达最坏情况,那么我们为什么不筛合数来避免这种情况呢

刚才说了,判断一个数是否为合数,就看它是不是其他数的倍数(除1和自身),但是我们不用除法,而是用加法(乘法)

枚举每一个因子,看他的倍数是否在区间内,如果在,那么该数为区间内的合数

而依次增加倍数的过程,其实也就是一个加法的过程,每次加上该因子,就相当于多了一倍,并且加法也比乘法快

现在来分析一下时间复杂度,为什么这么做是可行的?

我们枚举每一个因子(以下统称x),是从2到√区间右端点(若区间为(l,r),那么为√r),这样每一个因子都不会漏掉

而逐次增加倍数的过程呢,r除以x即为增加的次数,超过区间直接退出就好,也就是说时间复杂度为√r*(r/x),基本上相当于 r的复杂度,对于该题而言这当然也不可取(r最大为1e9),我们需要做一些优化

对于区间外的数,我们并不关心它是否为合数(质数),所以枚举的倍数不是从2开始,而是从l(左端点)/x开始,这么处理就可以让倍数最快达到区间内,然后依次筛出区间内x的倍数(也就是合数)

如此处理后,增加的次数就变成了m(区间长度)/x,如此一来时间复杂度就变为√r*(m/x),平均下来差不多就是m的复杂度了(实际上要多一点),这样的处理的话时间复杂度就可以接受了,接下来说一些细节

枚举倍数的起点的时候,最少要从2x开始,因为x单独一个数不能判断是不是合数,所以取起点的时候就取(2x,l/x*x)的最大值就好了,然后就要把增加倍数的过程中增加到的数都要标记一下(合数),虽然l和r很大但是区间长度不大,映射一下就好了,具体可以用哈希来实现,我是直接把区间映射到从1到n

筛完合数,没被标记的就是质数了,剩下的就简单了,完工!

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 1e6 + 10;

bool st[N];
ll n, m, l, r, p, q;
ll ans, res, pre;

int main()
{
    while(scanf("%lld%lld", &n, &m), n && m)
    {
        p = sqrt(m);
        for(int i = 1; i <= m - n + 1; ++ i) st[i] = 0;
        if(n == 1) st[1] = 1;
        for(int i = 2; i <= p; ++ i)
        {
            q = max(2ll, n / i) * i;
            while(q <= m)
            {
                if(q >= n) st[q - n + 1] = 1;
                q += i;
            }
        }

        ans = N;
        pre = res = 0;
        for(int i = 1; i <= m - n + 1; ++ i)
            if(!st[i])
            {
                if(pre)
                {
                    if(i - pre < ans)
                    {
                        l = pre;
                        ans = i - pre;
                    }
                    if(i - pre > res)
                    {
                        r = pre;
                        res = i - pre;
                    }
                }
                pre = i;
            }

        l += n - 1;
        r += n - 1;
        if(res) printf("%lld,%lld are closest, %lld,%lld are most distant.\n", l, l + ans, r, r + res);
        else puts("There are no adjacent primes.");

        n = m = 0;
    }

    return 0;
}

0 评论

你确定删除吗?
1024
x

© 2018-2023 AcWing 版权所有  |  京ICP备17053197号-1
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标
请输入绑定的邮箱地址
请输入注册信息