LightOj 1197. Help Hanzo

Help Hanzo

题意

给定区间 \([l, r]\) ,求区间内质数的数量。
\(1 \le l, r \le 10^9, 1 \le r - l + 1 \le 10^6\)

分析

由于 \(l\)\(r\) 都是 \(10^9\) 级别的,所以不能直接使用欧拉筛。

注意到 \([l, r]\) 区间长度很小。

在欧拉筛中,每个合数都用最小的质因子来筛去。由于题目数据最大所需要筛去的数字为 \(10^9\) ,那么对于每个数字我们只需要用到最大 \(\sqrt{10^9} = 10^5\) 级别的质数来筛去。

那么我们可以先处理出 \([1, 10^5]\) 中的所有质数,再用这些质数去筛去 \([l, r]\) 中的质数。

细节处很多,在代码中展示。

Code

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N = 1000010; // 最坏情况 1e6 级别内超过 1e5 个质数,所以不能只开 1e5 空间

int prime[N], cnt;
bool st[N];
void get_primes (int n) {
    for (int i = 2; i <= n; i ++ ) {
        if (!st[i]) prime[cnt ++ ] = i;
        for (int j = 0; i * prime[j] <= n; j ++ ) {
            st[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
}

signed main ()
{
    int T; cin >> T; for (int t = 1; t <= T; t ++ )
    {
        memset(st, 0, sizeof st); cnt = 0; // 每组数据都要初始化
        get_primes(50000);
        ll l, r; cin >> l >> r;
        // 用 [1, 50000] 中的所有质数去筛掉区间合数
        memset(st, 0, sizeof st); // 用st重新筛区间数字
        for (ll i = 0, p; i < cnt && (p = prime[i]); i ++ )
            for (ll j = max((l + p - 1) / p * p, p * 2ll); j <= r; j += p)
            /*
             * 用某个质数去筛掉区间内的合数,那么这个筛去的数字至少为 2 * p
             * 如果不在区间,则要筛去最小的在区间内的倍数级别,向上取整为 (l + p - 1) / p * p
             * 所以要取max,注意要开ll,因为 j += p 可能爆int
             */
                st[j - l] = true;

        cnt = 0;
        for (int i = l; i <= r; i ++ )
            // 坑点,1在筛的时候是没有被筛去的,但是1不是质数,所以要保证 i >= 2
            if (!st[i - l] && i >= 2) prime[cnt ++ ] = i;

        cout << "Case " << t << ": " << cnt << '\n';
    }
    return 0;
}
posted @ 2021-11-20 15:26  Horb7  阅读(20)  评论(0编辑  收藏  举报