Facebook Pixel

3233. Find the Count of Numbers Which Are Not Special

MediumArrayMathNumber Theory
Leetcode Link

Problem Description

You are given two positive integers l and r that define a range [l, r].

For any number x, the proper divisors of x are all positive divisors of x except x itself. For example:

  • The proper divisors of 4 are 1 and 2 (excluding 4 itself)
  • The proper divisors of 6 are 1, 2, and 3 (excluding 6 itself)

A number is called special if it has exactly 2 proper divisors.

Examples:

  • The number 4 is special because it has exactly 2 proper divisors: 1 and 2
  • The number 6 is not special because it has 3 proper divisors: 1, 2, and 3

Your task is to count how many numbers in the range [l, r] are not special.

The key observation is that only perfect squares of prime numbers are special. This is because if p is prime, then has exactly three divisors: 1, p, and . Since proper divisors exclude itself, we get exactly 2 proper divisors (1 and p). All other numbers either have fewer than 2 or more than 2 proper divisors.

The solution uses the Sieve of Eratosthenes to precompute all prime numbers up to √(10⁹) (since the maximum value of r is 10⁹). Then it counts how many primes exist in the range [⌈√l⌉, ⌊√r⌋], as the squares of these primes would be the special numbers in [l, r]. The final answer is the total count of numbers in [l, r] minus the count of special numbers.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about what makes a number have exactly 2 proper divisors.

First, consider the divisor structure of different types of numbers:

  • Prime numbers like 3 have divisors: 1, 3. So proper divisors: just 1 (only 1 proper divisor)
  • Composite numbers like 6 have divisors: 1, 2, 3, 6. So proper divisors: 1, 2, 3 (3 proper divisors)
  • Perfect squares like 9 have divisors: 1, 3, 9. So proper divisors: 1, 3 (2 proper divisors)

This pattern reveals something interesting: numbers with exactly 2 proper divisors seem to have exactly 3 total divisors.

What numbers have exactly 3 divisors? Let's think about the prime factorization. If a number n = p₁^a₁ × p₂^a₂ × ... × pₖ^aₖ, then the number of divisors is (a₁ + 1) × (a₂ + 1) × ... × (aₖ + 1).

For this product to equal 3, and since 3 is prime, we need exactly one term equal to 3 and all others equal to 1. This means we need one exponent to be 2 and all others to be 0. In other words, n = p² for some prime p.

So special numbers are exactly the squares of primes! For example:

  • 4 = 2² has divisors 1, 2, 4 → proper divisors: 1, 2 ✓
  • 9 = 3² has divisors 1, 3, 9 → proper divisors: 1, 3 ✓
  • 25 = 5² has divisors 1, 5, 25 → proper divisors: 1, 5 ✓

Now, to count non-special numbers in [l, r], we need to count how many squares of primes fall in this range. If is in [l, r], then √l ≤ p ≤ √r. So we need to count primes in the range [⌈√l⌉, ⌊√r⌋].

This leads us to precompute all primes up to √(10⁹) ≈ 31623 using the Sieve of Eratosthenes, then count how many of these primes squared would fall in our range.

Learn more about Math patterns.

Solution Approach

The implementation consists of two main parts: preprocessing prime numbers and counting special numbers in the given range.

Step 1: Precompute Prime Numbers Using Sieve of Eratosthenes

We first calculate all prime numbers up to m = 31623 (which is approximately √(10⁹)). This upper bound is chosen because the maximum value of r can be 10⁹, and we need to check primes whose squares could be at most 10⁹.

m = 31623
primes = [True] * (m + 1)
primes[0] = primes[1] = False
for i in range(2, m + 1):
    if primes[i]:
        for j in range(i + i, m + 1, i):
            primes[j] = False

The sieve works by:

  • Initially marking all numbers as potentially prime (True)
  • Setting 0 and 1 as non-prime (False)
  • For each prime number i, marking all its multiples (i+i, i+2i, i+3i, ...) as non-prime

Step 2: Count Special Numbers in Range [l, r]

For the given range [l, r], we need to find how many squares of primes fall within it.

def nonSpecialCount(self, l: int, r: int) -> int:
    lo = ceil(sqrt(l))
    hi = floor(sqrt(r))
    cnt = sum(primes[i] for i in range(lo, hi + 1))
    return r - l + 1 - cnt

The logic here:

  • Calculate lo = ⌈√l⌉ and hi = ⌊√r⌋ to find the range of potential primes
  • If p is a prime in [lo, hi], then is a special number in [l, r]
  • Count all primes in the range [lo, hi] using our precomputed sieve
  • The total count of numbers in [l, r] is r - l + 1
  • Subtract the count of special numbers to get the count of non-special numbers

Example Walkthrough:

For l = 5, r = 30:

  • lo = ⌈√5⌉ = 3 and hi = ⌊√30⌋ = 5
  • Check primes in range [3, 5]: 3 and 5 are prime
  • Special numbers: 3² = 9 and 5² = 25
  • Total numbers in [5, 30]: 30 - 5 + 1 = 26
  • Non-special numbers: 26 - 2 = 24

The time complexity is O(m log log m) for the sieve preprocessing and O(√r - √l) for counting, where m = 31623.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with l = 8 and r = 20.

Step 1: Identify what we're looking for We need to count how many numbers in [8, 20] are NOT special. Remember, a number is special if it has exactly 2 proper divisors, which only happens for squares of prime numbers.

Step 2: Find the range of potential primes

  • Calculate lo = ⌈√8⌉ = ⌈2.83...⌉ = 3
  • Calculate hi = ⌊√20⌋ = ⌊4.47...⌋ = 4
  • So we need to check primes in the range [3, 4]

Step 3: Identify primes in [3, 4] Using our precomputed sieve:

  • Is 3 prime? Yes ✓
  • Is 4 prime? No (4 = 2 × 2)

So we have exactly 1 prime (which is 3) in our range.

Step 4: Find special numbers The square of our prime gives us the special number:

  • 3² = 9 (which is in range [8, 20] ✓)

Let's verify that 9 is indeed special:

  • Divisors of 9: {1, 3, 9}
  • Proper divisors of 9: {1, 3} (excluding 9 itself)
  • Count of proper divisors: 2 ✓

Step 5: Count non-special numbers

  • Total numbers in [8, 20]: 20 - 8 + 1 = 13
  • Special numbers found: 1 (which is 9)
  • Non-special numbers: 13 - 1 = 12

Verification: The numbers in [8, 20] are: 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20

  • Only 9 is special (square of prime 3)
  • The other 12 numbers are not special

Therefore, the answer is 12.

Solution Implementation

1from math import ceil, floor, sqrt
2
3# Pre-compute all prime numbers up to 31623 using Sieve of Eratosthenes
4# 31623 is approximately sqrt(10^9), suitable for checking if sqrt of numbers up to 10^9 is prime
5MAX_LIMIT = 31623
6is_prime = [True] * (MAX_LIMIT + 1)
7is_prime[0] = is_prime[1] = False  # 0 and 1 are not prime
8
9# Sieve of Eratosthenes algorithm
10for i in range(2, MAX_LIMIT + 1):
11    if is_prime[i]:
12        # Mark all multiples of i as non-prime
13        for j in range(i * 2, MAX_LIMIT + 1, i):
14            is_prime[j] = False
15
16
17class Solution:
18    def nonSpecialCount(self, l: int, r: int) -> int:
19        """
20        Count non-special numbers in the range [l, r].
21        A special number is a perfect square whose square root is prime.
22      
23        Args:
24            l: Left boundary of the range (inclusive)
25            r: Right boundary of the range (inclusive)
26      
27        Returns:
28            Count of non-special numbers in the range
29        """
30        # Find the range of potential square roots
31        # We need to check if numbers from l to r are perfect squares of primes
32        lower_bound = ceil(sqrt(l))
33        upper_bound = floor(sqrt(r))
34      
35        # Count how many primes have their squares in the range [l, r]
36        special_count = sum(is_prime[i] for i in range(lower_bound, upper_bound + 1))
37      
38        # Total numbers minus special numbers equals non-special numbers
39        total_numbers = r - l + 1
40        return total_numbers - special_count
41
1class Solution {
2    // Upper bound for prime numbers (sqrt of 10^9 is approximately 31623)
3    private static final int MAX_PRIME = 31623;
4  
5    // Boolean array to mark prime numbers using Sieve of Eratosthenes
6    private static boolean[] isPrime = new boolean[MAX_PRIME + 1];
7  
8    // Static initialization block to precompute all prime numbers up to MAX_PRIME
9    static {
10        // Initially mark all numbers as prime
11        Arrays.fill(isPrime, true);
12      
13        // 0 and 1 are not prime numbers
14        isPrime[0] = false;
15        isPrime[1] = false;
16      
17        // Sieve of Eratosthenes algorithm
18        for (int i = 2; i <= MAX_PRIME; i++) {
19            if (isPrime[i]) {
20                // Mark all multiples of i as non-prime
21                for (int j = i + i; j <= MAX_PRIME; j += i) {
22                    isPrime[j] = false;
23                }
24            }
25        }
26    }
27  
28    /**
29     * Counts the number of non-special integers in the range [l, r].
30     * A special number is the square of a prime number.
31     * 
32     * @param l the lower bound of the range (inclusive)
33     * @param r the upper bound of the range (inclusive)
34     * @return the count of non-special numbers in the range
35     */
36    public int nonSpecialCount(int l, int r) {
37        // Find the smallest integer whose square is >= l
38        int lowerBound = (int) Math.ceil(Math.sqrt(l));
39      
40        // Find the largest integer whose square is <= r
41        int upperBound = (int) Math.floor(Math.sqrt(r));
42      
43        // Count how many primes exist in the range [lowerBound, upperBound]
44        // These primes, when squared, give us special numbers in [l, r]
45        int specialCount = 0;
46        for (int i = lowerBound; i <= upperBound; i++) {
47            if (isPrime[i]) {
48                specialCount++;
49            }
50        }
51      
52        // Total numbers in range minus special numbers gives non-special count
53        int totalNumbers = r - l + 1;
54        return totalNumbers - specialCount;
55    }
56}
57
1// Global constant for the upper bound of prime sieve
2const int MAX_PRIME = 31623; // sqrt(10^9) ≈ 31623, sufficient for checking primes up to sqrt(r)
3
4// Global array to mark prime numbers using Sieve of Eratosthenes
5bool isPrime[MAX_PRIME + 1];
6
7// Lambda function for one-time initialization of the prime sieve
8// This runs automatically before main() due to the assignment to 'init'
9auto init = [] {
10    // Initialize all numbers as potentially prime
11    memset(isPrime, true, sizeof(isPrime));
12  
13    // 0 and 1 are not prime numbers
14    isPrime[0] = isPrime[1] = false;
15  
16    // Sieve of Eratosthenes algorithm
17    for (int i = 2; i <= MAX_PRIME; ++i) {
18        if (isPrime[i]) {
19            // Mark all multiples of i as non-prime
20            for (int j = i * 2; j <= MAX_PRIME; j += i) {
21                isPrime[j] = false;
22            }
23        }
24    }
25    return 0;
26}();
27
28class Solution {
29public:
30    /**
31     * Counts non-special numbers in the range [l, r]
32     * A special number is the square of a prime number
33     * 
34     * @param l: left boundary of the range (inclusive)
35     * @param r: right boundary of the range (inclusive)
36     * @return: count of non-special numbers in [l, r]
37     */
38    int nonSpecialCount(int l, int r) {
39        // Find the range of numbers whose squares could be in [l, r]
40        int lowerBound = ceil(sqrt(l));    // Smallest integer whose square >= l
41        int upperBound = floor(sqrt(r));   // Largest integer whose square <= r
42      
43        // Count how many primes exist in [lowerBound, upperBound]
44        // Their squares will be special numbers in [l, r]
45        int specialCount = 0;
46        for (int i = lowerBound; i <= upperBound; ++i) {
47            if (isPrime[i]) {
48                ++specialCount;
49            }
50        }
51      
52        // Total numbers in range minus special numbers gives non-special count
53        int totalNumbers = r - l + 1;
54        return totalNumbers - specialCount;
55    }
56};
57
1// Maximum value for prime sieve (approximately sqrt(10^9))
2const MAX_PRIME_LIMIT = 31623;
3
4// Boolean array to track prime numbers using Sieve of Eratosthenes
5const isPrime: boolean[] = Array(MAX_PRIME_LIMIT + 1).fill(true);
6
7// Initialize the prime sieve using an IIFE (Immediately Invoked Function Expression)
8(() => {
9    // 0 and 1 are not prime numbers
10    isPrime[0] = isPrime[1] = false;
11  
12    // Sieve of Eratosthenes algorithm
13    for (let i = 2; i <= MAX_PRIME_LIMIT; ++i) {
14        if (isPrime[i]) {
15            // Mark all multiples of i as non-prime
16            for (let j = i * 2; j <= MAX_PRIME_LIMIT; j += i) {
17                isPrime[j] = false;
18            }
19        }
20    }
21})();
22
23/**
24 * Counts the number of non-special integers in the range [l, r]
25 * A special number is a perfect square whose square root is a prime number
26 * 
27 * @param l - The lower bound of the range (inclusive)
28 * @param r - The upper bound of the range (inclusive)
29 * @returns The count of non-special numbers in the range
30 */
31function nonSpecialCount(l: number, r: number): number {
32    // Calculate the range of potential square roots
33    const lowerBound = Math.ceil(Math.sqrt(l));
34    const upperBound = Math.floor(Math.sqrt(r));
35  
36    // Count how many primes exist in the square root range
37    let specialCount = 0;
38    for (let i = lowerBound; i <= upperBound; ++i) {
39        if (isPrime[i]) {
40            // i^2 is a special number since i is prime
41            ++specialCount;
42        }
43    }
44  
45    // Total numbers in range minus special numbers equals non-special numbers
46    return r - l + 1 - specialCount;
47}
48

Time and Space Complexity

The code consists of two parts: preprocessing (Sieve of Eratosthenes) and the main solution.

Preprocessing (Sieve of Eratosthenes):

  • Time Complexity: O(m log log m) where m = 31623. This is because for each prime i, we mark its multiples as non-prime, and the sum of m/i for all primes i ≤ m is approximately m log log m.
  • Space Complexity: O(m) for the primes array of size m + 1.

Main Solution (nonSpecialCount method):

  • Time Complexity: O(√r - √l) which is at most O(√r). Since r can be up to 10^9, this is O(√10^9) = O(31623) = O(√m) where m = 10^9.
  • Space Complexity: O(1) for the method itself (only using a few variables).

Overall Complexity:

  • Time Complexity: The preprocessing runs once with O(m log log m) where m = 31623 ≈ √10^9, and each query runs in O(√r - √l). Since 31623 = √10^9, we can express this as O(√m log log √m) = O(√m log log m) where m = 10^9. However, for practical purposes and considering that log log √m is a very small constant, this simplifies to O(√m) where m = 10^9.
  • Space Complexity: O(31623) = O(√m) where m = 10^9, for storing the prime sieve.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Boundary Calculation for Square Roots

One of the most common mistakes is incorrectly calculating the range of potential primes whose squares fall within [l, r].

Pitfall Example:

# WRONG: Using simple division or incorrect rounding
lower_bound = int(l ** 0.5)  # May miss edge cases
upper_bound = int(r ** 0.5)  # May miss valid primes

Why it's wrong:

  • For l = 5, using int(5 ** 0.5) = 2 would check if 2² = 4 is in range, but 4 < 5
  • For r = 30, using int(30 ** 0.5) = 5 works, but for r = 25, int(25 ** 0.5) = 5 and 5² = 25 should be included

Correct Solution:

from math import ceil, floor, sqrt
lower_bound = ceil(sqrt(l))   # Ensures p² >= l
upper_bound = floor(sqrt(r))  # Ensures p² <= r

2. Off-by-One Errors in Range Iteration

Pitfall Example:

# WRONG: Forgetting to include upper_bound
special_count = sum(is_prime[i] for i in range(lower_bound, upper_bound))

Correct Solution:

# Include upper_bound by adding 1
special_count = sum(is_prime[i] for i in range(lower_bound, upper_bound + 1))

3. Insufficient Sieve Size

Pitfall Example:

# WRONG: Using a smaller sieve size
MAX_LIMIT = 1000  # Too small for r up to 10^9

Why it's wrong: If r = 10^9, we need to check primes up to √(10^9) ≈ 31623. A smaller sieve would miss valid primes.

Correct Solution:

MAX_LIMIT = 31623  # Covers sqrt(10^9)

4. Floating Point Precision Issues

Pitfall Example:

# WRONG: Direct floating point comparison
if sqrt(n) == int(sqrt(n)):  # May have precision errors
    # Check if it's a perfect square

Better Approach: The solution avoids this by working with the range of square roots directly rather than checking individual numbers for being perfect squares.

5. Global Variable Scope Issues

Pitfall Example:

class Solution:
    def nonSpecialCount(self, l: int, r: int) -> int:
        # WRONG: Computing sieve inside the method for each call
        MAX_LIMIT = 31623
        is_prime = [True] * (MAX_LIMIT + 1)
        # ... sieve computation ...

Why it's wrong: This recomputes the sieve for every function call, which is inefficient for multiple test cases.

Correct Solution: Precompute the sieve once outside the class as a global variable, making it available for all test cases without recomputation.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What is the best way of checking if an element exists in a sorted array once in terms of time complexity? Select the best that applies.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More