開啟章節選單

10539 Almost Prime Numbers

題目敘述

題目說明:Almost Prime Numbers(近似質數)

一個數若是某個質數的整數次方(且次方 ≥ 2),這種數我們稱為「近似質數(Almost Prime)」,也就是:

p^k,其中 p 是質數,k ≥ 2 且 p^k ≤ 10¹²
給定多組查詢範圍,請找出在每個範圍內有多少個「近似質數」。

輸入說明

第一行:一個整數 N(1 ≤ N ≤ 600),代表查詢次數
接下來 N 行:每行兩個整數 low 和 high(0 < low ≤ high < 10¹²)

輸出說明

對每一組查詢,輸出該範圍內的近似質數個數(包含 low 與 high)

解題思路

這題要求計算區間 [low,high] 中有多少個「幾乎質數(almost prime)」,即形如的數,其中: 由於查詢次數最多為 600 次,而範圍最大到,我們無法暴力每次都重新計算,因此採取 預處理 + 二分搜尋 的策略,大致分為以下三個步驟:

步驟一:建立質數表(pbuide())
利用線性篩法(Linear Sieve)來建立以下的質數表儲存所有質數在 ps 向量中

步驟二:預先生成所有幾乎質數(generate_almost_primes())
對每個質數p²開始乘上自己,不斷生成直到大於10¹²停止,所有合法的p²加入 almost_primes 向量中,排序這個列表,方便之後查詢用二分搜尋

步驟三:對每組區間使用二分搜尋查詢(count_almost_primes())
使用 lower_bound 和 upper_bound 快速找出範圍 [low,high] 中的幾乎質數個數

程式碼

#include <bits/stdc++.h>
using namespace std;

const long long PEND = 1e6;      // 質數範圍 (10^6)
const long long UPPER_LIMIT = 1e12; // 幾乎質數最大範圍 (10^12)

bitset<PEND> isp;
vector<long long> ps;            // 質數列表
vector<long long> almost_primes; // 幾乎質數列表

// 線性篩法建質數表
void pbuide() {
    isp.set();
    isp[0] = isp[1] = 0;
    for (long long i = 2; i < PEND; i++) {
        if (isp[i]) ps.push_back(i);
        for (auto &p : ps) {
            if (i * p >= PEND) break;
            isp[i * p] = 0;
            if (i % p == 0) break;
        }
    }
}

// 生成所有幾乎質數 (p^k),確保不超過 10^12
void generate_almost_primes() {
    for (long long p : ps) {
        long long x = p * p; // 幾乎質數最小為 p^2
        while (x <= UPPER_LIMIT) {
            almost_primes.push_back(x);
            x *= p;
        }
    }
    sort(almost_primes.begin(), almost_primes.end()); // 排序以利二分搜尋
}

// 計算 [low, high] 內的幾乎質數數量
int count_almost_primes(long long low, long long high) {
    return upper_bound(almost_primes.begin(), almost_primes.end(), high) -
           lower_bound(almost_primes.begin(), almost_primes.end(), low);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    pbuide();                  // 建立質數表
    generate_almost_primes();  // 生成幾乎質數

    int n;
    cin >> n;
    while (n--) {
        long long a, b;
        cin >> a >> b;
        cout << count_almost_primes(a, b) << "\n";
    }

    return 0;
}