All Euler problems
Project Euler

Perfect Power Detection

A perfect power is an integer of the form a^b where a >= 1 and b >= 2. Find the number of distinct perfect powers in {1, 2,..., 10^18}.

Source sync Apr 19, 2026
Problem #0976
Level Level 39
Solved By 88
Languages C++, Python
Answer 675608326
Length 299 words
number_theorygeometrysearch

Problem Statement

This archive keeps the full statement, math, and original media on the page.

Two players X and O play a game with strips of squares of lengths , originally all blank.

Starting with X, they make moves in turn. At X's turn, X draws an "X" symbol; at O's turn, O draws an "O" symbol.
The symbol must be drawn in one blank square with either red or blue pen, subject to the following restrictions:

  1. two symbols in adjacent squares on one strip must be different symbols and must have different colour;
  2. if there is at least one blank strip, then one must draw on a blank strip.

Whoever does not have a valid move loses the game.

Let be the number of tuples such that , and that X has a winning strategy to the corresponding game.
For example, and .

Find .

Problem 976: Perfect Power Detection

Mathematical Foundation

Definition 1. Let P(N)={nZ:1nN,  n=ab for some a1,b2}\mathcal{P}(N) = \{n \in \mathbb{Z} : 1 \le n \le N, \; n = a^b \text{ for some } a \ge 1, b \ge 2\}. We seek P(N)|\mathcal{P}(N)| for N=1018N = 10^{18}.

Theorem 1 (Reduction to Prime Exponents). Every perfect power n=abn = a^b with b2b \ge 2 can be written as n=cpn = c^p for some prime pp and integer c1c \ge 1.

Proof. Write b=pmb = pm where pp is a prime factor of bb. Then n=ab=(am)p=cpn = a^b = (a^m)^p = c^p with c=amc = a^m. \square

Theorem 2 (Inclusion-Exclusion Formula). Let Pp(N)={nN:n=ap for some a1}P_p(N) = \{n \le N : n = a^p \text{ for some } a \ge 1\}, so Pp(N)=N1/p|P_p(N)| = \lfloor N^{1/p} \rfloor. Then:

P(N)=p primePp(N)=SQ(1)S+1N1/pSp|\mathcal{P}(N)| = \left|\bigcup_{p \text{ prime}} P_p(N)\right| = \sum_{\emptyset \neq S \subseteq \mathcal{Q}} (-1)^{|S|+1} \left\lfloor N^{1/\prod_{p \in S} p} \right\rfloor

where Q={p prime:plog2N}\mathcal{Q} = \{p \text{ prime} : p \le \log_2 N\} is the set of relevant prime exponents.

Proof. By inclusion-exclusion on the sets Pp(N)P_p(N). A number in Pp1Pp2PpkP_{p_1} \cap P_{p_2} \cap \cdots \cap P_{p_k} (with pip_i distinct primes) is an lcm(p1,,pk)\text{lcm}(p_1,\ldots,p_k)-th power, i.e., a (p1p2pk)(p_1 p_2 \cdots p_k)-th power (since the pip_i are distinct primes). Thus Pp1Ppk=N1/(p1pk)|P_{p_1} \cap \cdots \cap P_{p_k}| = \lfloor N^{1/(p_1\cdots p_k)} \rfloor. The union has no contribution from primes p>log2Np > \log_2 N since N1/p=1\lfloor N^{1/p} \rfloor = 1 and 1=1p1 = 1^p is already counted. \square

Lemma 1 (Exponent Bound). For N=1018N = 10^{18}, the relevant primes are Q={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59}\mathcal{Q} = \{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59\}, since log2(1018)59.79\log_2(10^{18}) \approx 59.79.

Proof. If p61p \ge 61 is prime, then (1018)1/p(1018)1/61=1\lfloor (10^{18})^{1/p} \rfloor \le \lfloor (10^{18})^{1/61} \rfloor = 1, so PpP_p contributes only {1}\{1\}, which is already in every PqP_q. \square

Lemma 2 (Truncation of Inclusion-Exclusion). For N=1018N = 10^{18}, any subset SQS \subseteq \mathcal{Q} with pSp>59\prod_{p \in S} p > 59 satisfies N1/p=1\lfloor N^{1/\prod p} \rfloor = 1. Thus only subsets with product 59\le 59 contribute nontrivially.

Proof. If pSp=e>59\prod_{p \in S} p = e > 59, then N1/e<N1/60=1018/60=100.32N^{1/e} < N^{1/60} = 10^{18/60} = 10^{0.3} \approx 2, so N1/e=1\lfloor N^{1/e} \rfloor = 1. These terms contribute a net correction that must be carefully tracked (adding/subtracting 1). \square

Editorial

Count the number of distinct perfect powers a^b <= 10^18, where a >= 1 and b >= 2. A perfect power is an integer that can be expressed as a^b for integers a >= 1, b >= 2. Numbers like 64 = 2^6 = 4^3 = 8^2 should only be counted once. We relevant prime exponents. We then iterate over all non-empty subsets of primes where product <= log2(N). Finally, use inclusion-exclusion.

Pseudocode

Relevant prime exponents
Iterate over all non-empty subsets of primes where product <= log2(N)
Use inclusion-exclusion
for each non-empty subset S of primes
floor(N^{1/e}) = 1, handle separately
else
Handle the subsets with large products (contribute +/- 1)
by careful enumeration
Note: N^{1/e} must be computed with care for large N
Use integer Newton's method: find largest a such that a^e <= N
Adjust: check a^e <= N < (a+1)^e

Complexity Analysis

  • Time: O(2Q)O(2^{|\mathcal{Q}|}) subsets to enumerate, but heavy truncation (most subsets have product >59> 59 and are trivial) reduces this drastically. The dominant cost is N1/2=109\lfloor N^{1/2} \rfloor = 10^9 only as a value, not a loop. Each IntegerRoot call is O(logN)O(\log N) Newton iterations with O(elogN)O(e \log N)-bit arithmetic. In practice: O(2QO(2^{|\mathcal{Q}|}) with Q=17|\mathcal{Q}| = 17, giving 131072\sim 131072 subsets, each with constant-time root computation. Total: O(217logN)O(107)O(2^{17} \log N) \approx O(10^7).
  • Space: O(Q)=O(logN/loglogN)O(|\mathcal{Q}|) = O(\log N / \log\log N).

Answer

675608326\boxed{675608326}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_976/solution.cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
    // For 10^18 we need careful computation
    // Count distinct a^b with a>=1, b>=2, a^b <= 10^18
    // Use set of (base reduced to primitive form)
    long long N = 1000000000000000000LL;
    set<long long> seen;
    seen.insert(1);
    for (int b = 2; b <= 60; b++) {
        long long a = 2;
        while (true) {
            // Compute a^b, watch for overflow
            long long val = 1;
            bool overflow = false;
            for (int i = 0; i < b; i++) {
                if (val > N / a) { overflow = true; break; }
                val *= a;
            }
            if (overflow || val > N) break;
            seen.insert(val);
            a++;
        }
    }
    cout << (long long)seen.size() << endl;
    return 0;
}