結果
| 問題 |
No.1322 Totient Bound
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:18:40 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
TLE
|
| 実行時間 | - |
| コード長 | 11,744 bytes |
| コンパイル時間 | 1,524 ms |
| コンパイル使用メモリ | 116,944 KB |
| 実行使用メモリ | 38,564 KB |
| 最終ジャッジ日時 | 2025-05-14 13:19:35 |
| 合計ジャッジ時間 | 9,728 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 15 TLE * 1 -- * 20 |
ソースコード
#include <iostream>
#include <vector>
#include <map>
#include <cmath>
#include <string>
#include <algorithm> // for std::reverse
// Use GCC/Clang built-in 128 bit integer type if available
#ifdef __GNUC__
typedef __uint128_t u128;
#else
// Fallback for other compilers, might not work for large N
typedef unsigned long long u128;
#warning "Using unsigned long long for 128-bit type. May overflow for large N."
#endif
typedef long long ll;
ll N_ll; // Input N (up to 10^10)
u128 N; // Use 128 bit integer type for N and calculations involving it
std::vector<int> primes; // Stores precomputed primes
// Bound for precomputed primes. sqrt(10^10) = 10^5.
// A slightly larger bound might provide safety or minor speedup.
const int PRIME_BOUND = 150000;
// Memoization tables for the recursive functions
// Key: pair of (index k / prime value p, current limit M)
// Value: computed count S(k, M) or S_large(p, M)
std::map<std::pair<int, u128>, u128> memo;
std::map<std::pair<ll, u128>, u128> memo_large;
// Primality test using trial division by precomputed primes
// Sufficient for numbers up to PRIME_BOUND^2. For N=10^10, need check up to sqrt(N)=10^5.
// PRIME_BOUND=150k ensures this check is sufficient for primes p s.t. p*p <= N+1
bool is_prime(ll n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
// Check divisibility by precomputed primes up to sqrt(n)
for (int p : primes) {
// Optimization: only check primes up to sqrt(n)
if ((ll)p * p > n) break;
if (n % p == 0) return false;
}
// If n survived trial division by primes up to sqrt(n), it's prime.
return true;
}
// Find the smallest prime strictly greater than p
ll get_next_prime(ll p) {
ll num = p + 1;
// Handle edge cases and start checking from an odd number > p
if (num <= 2) num = 2;
else if (num % 2 == 0) num++;
while(true) {
// Basic overflow check for signed long long
if (num < p) return -1;
if (is_prime(num)) return num;
// Check for potential overflow before adding 2
// Use LLONG_MAX from <limits> for robustness if needed
if (num <= 9223372036854775807LL - 2) {
num += 2;
} else {
return -1; // Overflow potential
}
}
}
// Forward declaration for the main recursive function S
u128 S(int k, u128 M);
/**
* Recursive function to count numbers x such that phi(x) <= M,
* where all prime factors of x are >= current_p.
* This function handles primes larger than PRIME_BOUND.
* It includes x=1 in its count.
*/
u128 S_large(ll current_p, u128 M) {
// Base case: If M is 0, no positive integer x satisfies phi(x) <= 0.
if (M == 0) return 0;
// Ensure current_p is at least 2. If it's <= 1, start with 2.
if (current_p <= 1) current_p = 2;
// Base case: If the smallest possible prime factor p leads to phi(p) = p-1 > M,
// then only x=1 can satisfy the condition.
// Check `current_p - 1 > M` is sufficient. `current_p > M + 1` implies this for M>=0.
if (current_p - 1 > M) {
return 1; // Only x=1 is possible.
}
// Memoization check
std::pair<ll, u128> state = {current_p, M};
if (memo_large.count(state)) {
return memo_large[state];
}
// Initialize count result. Includes x=1.
u128 res = 1;
ll p = current_p;
// Iterate through primes p >= current_p
while(true) {
// If p-1 > M, then this prime and subsequent larger primes cannot be factors.
if (p - 1 > M) {
break;
}
u128 p_val = p; // Use u128 for calculations involving p
u128 phi_pa; // Stores phi(p^a)
// Correct initialization for phi(p^1)
if (p == 2) phi_pa = 1;
else phi_pa = p_val - 1;
// Iterate through powers 'a' of the current prime p
for (int a = 1; ; ++a) {
// If phi(p^a) exceeds M, this power and higher powers are invalid.
if (phi_pa > M) break;
u128 next_M = M / phi_pa; // Calculate the remaining limit for phi(y)
ll next_p = get_next_prime(p); // Find the next prime after p
if (next_p == -1) break; // Error or overflow finding next prime
// Recursive call: count numbers y with prime factors >= next_p s.t. phi(y) <= next_M
res += S_large(next_p, next_M);
// Prepare for the next power: Calculate phi(p^(a+1))
u128 next_phi_pa;
bool overflow = false;
// Calculation depends on whether p=2 or p is an odd prime
if (p == 2) {
// phi(2^1)=1, phi(2^2)=2, phi(2^3)=4, ... phi(2^a)=2^{a-1}
if (a==1) next_phi_pa = 2; // For a=2, phi is 2
else { // For a > 1, phi(2^{a+1}) = phi(2^a) * 2
// Check overflow before multiplying by 2
if (phi_pa > ((u128)-1) / 2) { overflow = true; }
else next_phi_pa = phi_pa * 2;
}
} else {
// For odd prime p, phi(p^{a+1}) = p * phi(p^a)
// Check overflow before multiplying by p
if (phi_pa > 0 && p > ((u128)-1) / phi_pa) { overflow = true; }
else next_phi_pa = phi_pa * p;
}
if (overflow) break; // Stop if overflow detected
// Optimization: Check if next phi value will exceed M using division check.
// This helps prune the search slightly earlier than the loop condition `phi_pa > M`.
// Check `phi_pa > M / p` approximate `next_phi_pa > M` check.
if (p > 1 && phi_pa > 0 && phi_pa > M / p) {
// Break if current phi_pa times p exceeds M.
// Only valid if p > 1. Avoid division by zero. phi_pa > 0 ensures M/p is well-defined.
// This check handles the update logic for both p=2 and p>2 implicitly.
break;
}
phi_pa = next_phi_pa; // Update phi_pa for the next iteration (a+1)
// Safety break for excessively large exponents (e.g., > 64 for 128-bit numbers)
if (a > 128) break;
}
// Move to the next prime for the outer loop
ll next_p_iter = get_next_prime(p);
// Break if error finding next prime or if potential infinite loop (next_p <= p)
if (next_p_iter == -1 || next_p_iter <= p) break;
p = next_p_iter;
}
// Store result in memoization table and return
return memo_large[state] = res;
}
/**
* Main recursive function to count numbers x such that phi(x) <= M.
* It uses precomputed primes up to PRIME_BOUND and then transitions to S_large.
* k is the index in the `primes` vector for the smallest prime factor allowed.
* Includes x=1 in its count.
*/
u128 S(int k, u128 M) {
// Base case: If M is 0, count is 0.
if (M == 0) return 0;
// If index k is beyond the precomputed primes list, switch to S_large
if (k >= primes.size()) {
// Find the prime immediately following the last precomputed prime
ll start_p = (primes.empty() ? 2 : get_next_prime(primes.back()));
if (start_p == -1) return 1; // Error case, assume only x=1
// Call S_large starting from this prime
return S_large(start_p, M);
}
// Memoization check
std::pair<int, u128> state = {k, M};
if (memo.count(state)) {
return memo[state];
}
// Result count initialized. Includes x=1 by definition of the recursion.
u128 res = 1;
// Iterate through precomputed primes starting from index k
for (int i = k; i < primes.size(); ++i) {
ll p = primes[i];
// If p-1 > M, this prime and subsequent larger primes cannot be factors.
if (p - 1 > M) {
break;
}
u128 p_val = p;
u128 phi_pa; // Stores phi(p^a)
// Initialize phi(p^1)
if (p == 2) phi_pa = 1;
else phi_pa = p_val - 1;
// Iterate through powers 'a' of prime p
for (int a = 1; ; ++a) {
// Safety check for phi_pa becoming 0 unexpectedly (should not happen for p>=2)
if (phi_pa == 0 && a > 1) break;
// If phi(p^a) exceeds M, this power and higher ones are invalid.
if (phi_pa > M) break;
u128 next_M = M / phi_pa; // Remaining limit for phi(y)
// Recursive call: count numbers y with smallest prime factor index >= i+1
// such that phi(y) <= next_M.
res += S(i + 1, next_M);
// Prepare for next power: Calculate phi(p^(a+1))
u128 next_phi_pa;
bool overflow = false;
// Calculation depends on p
if (p == 2) {
if (a==1) next_phi_pa = 2; // phi(2^2) = 2
else {
if (phi_pa > ((u128)-1) / 2) { overflow = true; }
else next_phi_pa = phi_pa * 2;
}
} else {
if (phi_pa > 0 && p > ((u128)-1) / phi_pa) { overflow = true; }
else next_phi_pa = phi_pa * p;
}
if (overflow) break;
// Optimization using division check to prune early
if (p > 1 && phi_pa > 0 && phi_pa > M / p) {
break;
}
phi_pa = next_phi_pa; // Update phi_pa for next iteration (a+1)
// Safety break for large exponents
if (a > 128) break;
}
}
// After iterating through precomputed primes in the range [k, primes.size()-1],
// we need to add the contribution from numbers whose smallest prime factor
// is larger than primes.back(). This is handled by calling S(primes.size(), M).
// Note: This call will naturally transition to S_large.
// We must subtract 1 because S(primes.size(), M) also counts x=1, which is already included in the initial `res = 1`.
res += S(primes.size(), M) - 1;
// Store result in memo table and return
return memo[state] = res;
}
// Overload output stream operator for u128 type for printing results
std::ostream& operator<<(std::ostream& os, u128 val) {
if (val == 0) return os << "0";
std::string s = "";
u128 temp = val;
while (temp > 0) {
s += (char)('0' + temp % 10);
temp /= 10;
}
std::reverse(s.begin(), s.end());
// Handle case where string might be empty if val was 0 initially (already checked)
// If val > 0 but string is empty (should not happen), print 0?
if (s.empty()) return os << "0";
return os << s;
}
// Sieve of Eratosthenes to precompute primes up to a given limit
void sieve(int limit) {
primes.clear();
std::vector<bool> is_p(limit + 1, true);
is_p[0] = is_p[1] = false;
for (int p = 2; p * p <= limit; ++p) {
if (is_p[p]) {
for (int i = p * p; i <= limit; i += p)
is_p[i] = false;
}
}
for (int p = 2; p <= limit; ++p) {
if (is_p[p]) {
primes.push_back(p);
}
}
}
int main() {
// Faster I/O
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
// Read input N
std::cin >> N_ll;
N = N_ll; // Convert to 128-bit type
// Precompute primes using Sieve
sieve(PRIME_BOUND);
// Clear memoization tables before starting calculation
memo.clear();
memo_large.clear();
// Call the main recursive function starting with index 0 and limit N
std::cout << S(0, N) << std::endl;
return 0;
}
qwewe