結果
問題 |
No.1653 Squarefree
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:14:09 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 6,751 bytes |
コンパイル時間 | 1,100 ms |
コンパイル使用メモリ | 95,552 KB |
実行使用メモリ | 136,960 KB |
最終ジャッジ日時 | 2025-05-14 13:15:28 |
合計ジャッジ時間 | 7,655 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | -- * 3 |
other | TLE * 1 -- * 37 |
ソースコード
#include <iostream> #include <vector> #include <cmath> #include <limits> // Required for std::numeric_limits #include <algorithm> // Required for std::max used in log calculation (optional optimization) // Define unsigned long long for clarity and brevity typedef unsigned long long ull; // Function to generate primes up to a given limit using a basic Sieve of Eratosthenes. // NOTE: This function is a placeholder. For the given constraints (R up to 10^18), // sqrt(R) can be up to 10^9. A simple sieve up to 10^9 is too slow and uses too much memory. // An efficient segmented sieve implementation is required for a solution that passes within time limits. // The provided sieve is suitable only for smaller limits (e.g., up to ~10^7). std::vector<ull> generate_primes(ull limit) { if (limit < 2) return {}; // No primes less than 2 // Create a boolean vector to mark non-prime numbers. is_p[i] = true means i is potentially prime. std::vector<bool> is_p(limit + 1, true); is_p[0] = is_p[1] = false; // 0 and 1 are not prime // Sieve of Eratosthenes for (ull p = 2; p * p <= limit; ++p) { if (is_p[p]) { // If p is prime // Mark all multiples of p as not prime, starting from p*p for (ull i = p * p; i <= limit; i += p) is_p[i] = false; } } // Collect all numbers marked as prime into a list std::vector<ull> primes_list; // Optimization: Reserve approximate space needed to reduce reallocations. // Prime Number Theorem approximation: pi(x) ~ x/ln(x). Add a small buffer. // Use std::max to avoid log(0) or log(1). if (limit >= 2) { primes_list.reserve(static_cast<size_t>(limit / log(std::max(2.0, (double)limit))) + 100); } for (ull p = 2; p <= limit; ++p) { if (is_p[p]) { primes_list.push_back(p); } } return primes_list; } int main() { // Use faster I/O operations std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); ull L, R; // Input range boundaries std::cin >> L >> R; // Calculate the total number of integers in the range [L, R] ull N = R - L + 1; // Create a boolean vector `is_marked` of size N. // `is_marked[i] = true` indicates that the integer L + i is divisible by a perfect square > 1. // Initially, assume all numbers are squarefree (not marked). std::vector<bool> is_marked(N, false); // Initialize the count of squarefree numbers to the total number of integers in the range. ull count = N; // Calculate floor(sqrt(R)) accurately using integer binary search to avoid floating point errors. ull sqrt_R = 0; ull low = 0, high = 1000000000ULL + 2; // Search space for sqrt(R). sqrt(10^18) = 10^9. Add buffer. while(low <= high){ ull mid = low + (high - low) / 2; // Calculate midpoint carefully to avoid overflow if (mid == 0) { // If mid is 0, it can't be sqrt(R) unless R=0 (not possible here as L>=1). // Continue search in the upper half [1, high]. low = mid + 1; continue; } // Check for potential overflow before calculating mid * mid. // If mid > ULLONG_MAX / mid, then mid * mid would overflow. bool potential_overflow = (mid > std::numeric_limits<ull>::max() / mid); if (potential_overflow || mid * mid > R) { // If mid*mid overflows or is greater than R, mid is too large. high = mid - 1; } else { // If mid*mid <= R, mid is a potential candidate for floor(sqrt(R)). // Store mid and try larger values. sqrt_R = mid; low = mid + 1; } } // Generate primes up to sqrt_R. // WARNING: This line is the main performance bottleneck for large R. // The included `generate_primes` function uses a basic sieve and will Time Limit Exceed // if sqrt_R is large (e.g., 10^9). A proper solution requires an optimized segmented sieve. std::vector<ull> primes_list; if (sqrt_R > 0) { // Call the prime generation function. Replace with optimized version if needed. primes_list = generate_primes(sqrt_R); } // Iterate through the generated primes p. We only need primes p such that p*p <= R. // Since we generated primes up to floor(sqrt(R)), this condition is implicitly satisfied. for (ull p : primes_list) { ull pp = p * p; // Calculate the square of the prime p. // Calculate the first multiple of pp that is greater than or equal to L. ull M_start; ull rem = L % pp; // Find the remainder when L is divided by pp. if (rem == 0) { M_start = L; // If L is a multiple of pp, it's the first multiple >= L. } else { // Otherwise, the first multiple is L + (pp - rem). This finds the next multiple of pp after L. // This calculation is safe from overflow as L <= 10^18, pp <= R <= 10^18. // L + pp <= 2*10^18 which fits in unsigned long long. M_start = L + (pp - rem); } // If the first multiple M_start is already greater than R, then no multiples of pp // exist in the range [L, R]. Skip to the next prime. if (M_start > R) { continue; } // Iterate through all multiples M of pp within the range [L, R], starting from M_start. for (ull M = M_start; M <= R; M += pp) { // Calculate the index corresponding to M in the `is_marked` vector. // M is guaranteed to be >= L, so idx >= 0. ull idx = M - L; // If the number M (at index idx) has not been marked yet, // it means this is the first time we found a square factor for it. // Mark it as not squarefree and decrement the count. if (!is_marked[idx]) { is_marked[idx] = true; // Mark L+idx as having a square factor > 1. count--; // Decrement the count of squarefree numbers. } // Check to prevent potential overflow when calculating the next multiple M + pp. // And break the loop if the next multiple would exceed R. // This check `pp > R - M` is equivalent to `M + pp > R` but safer against overflow. // Given R <= 10^18, M + pp will not overflow unsigned long long. if (pp > R - M) { break; // The next multiple M + pp would be > R. } } } // Output the final count of squarefree numbers in the range [L, R]. std::cout << count << std::endl; return 0; }