Cod sursa(job #3362667)

Utilizator Sorin123-21Enachioiu Sorin-Catalin Sorin123-21 Data 11 august 2026 12:29:49
Problema Ciurul lui Eratosthenes Scor 0
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 5.83 kb
#include <array>
#include <bit>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <utility>

constexpr int MAX_N = 2'000'000;
constexpr int ROOT = 1414;

// Păstrăm doar imparele:
// index 0 -> 1
// index 1 -> 3
// index 2 -> 5
// ...
constexpr std::size_t ODD_COUNT = (MAX_N + 1) / 2;

// Număr de impare per bloc.
constexpr std::size_t ODDS_PER_BLOCK = 1u << 16; // 65536

// 65536 biți / 64 = 1024 uint64_t
constexpr std::size_t WORDS_PER_BLOCK =
    ODDS_PER_BLOCK / 64;

constexpr std::size_t BLOCK_COUNT =
    (ODD_COUNT + ODDS_PER_BLOCK - 1) / ODDS_PER_BLOCK;


// ----------------------------------------------------
// Primele până la sqrt(MAX_N).
// E suficient de mic încât să fie calculat direct
// la compile-time.
// ----------------------------------------------------

consteval auto makeSmallSieve()
{
    std::array<bool, ROOT + 1> prime{};

    for (int i = 2; i <= ROOT; ++i) {
        prime[i] = true;
    }

    for (int p = 2; p * p <= ROOT; ++p) {
        if (!prime[p]) {
            continue;
        }

        for (int x = p * p; x <= ROOT; x += p) {
            prime[x] = false;
        }
    }

    return prime;
}

inline constexpr auto SMALL_PRIME = makeSmallSieve();


// ----------------------------------------------------

struct Block {
    std::array<std::uint64_t, WORDS_PER_BLOCK> bits{};
};


// ----------------------------------------------------
// Fiecare bloc este calculat într-o evaluare
// consteval separată.
// ----------------------------------------------------

template <std::size_t B>
consteval Block makeBlock()
{
    Block block{};

    for (auto& word : block.bits) {
        word = ~0ULL;
    }

    constexpr std::size_t firstIndex =
        B * ODDS_PER_BLOCK;

    constexpr std::size_t endIndex =
        (firstIndex + ODDS_PER_BLOCK < ODD_COUNT)
            ? firstIndex + ODDS_PER_BLOCK
            : ODD_COUNT;

    constexpr std::size_t validBits =
        endIndex - firstIndex;

    constexpr std::uint64_t low =
        2ULL * firstIndex + 1;

    constexpr std::uint64_t high =
        2ULL * (endIndex - 1) + 1;


    // Primul bloc conține 1.
    if constexpr (B == 0) {
        block.bits[0] &= ~1ULL;
    }


    // Marcăm compusele folosind doar primele impare
    // <= sqrt(MAX_N).
    for (int p = 3; p <= ROOT; p += 2) {

        if (!SMALL_PRIME[p]) {
            continue;
        }

        std::uint64_t first =
            static_cast<std::uint64_t>(p) * p;

        // Primul multiplu al lui p din bloc.
        if (first < low) {
            first =
                ((low + p - 1) / p) * p;
        }

        // Stocăm doar numere impare.
        if ((first & 1ULL) == 0) {
            first += p;
        }

        for (std::uint64_t x = first;
             x <= high;
             x += 2ULL * p) {

            const std::size_t index =
                static_cast<std::size_t>(
                    (x - low) >> 1
                );

            block.bits[index >> 6] &=
                ~(1ULL << (index & 63));
        }
    }


    // Ultimul bloc poate fi incomplet.
    if constexpr (validBits < ODDS_PER_BLOCK) {

        constexpr std::size_t fullWords =
            validBits / 64;

        constexpr unsigned remaining =
            validBits % 64;

        if constexpr (remaining != 0) {

            block.bits[fullWords] &=
                (1ULL << remaining) - 1;

            for (std::size_t i = fullWords + 1;
                 i < WORDS_PER_BLOCK;
                 ++i) {

                block.bits[i] = 0;
            }

        } else {

            for (std::size_t i = fullWords;
                 i < WORDS_PER_BLOCK;
                 ++i) {

                block.bits[i] = 0;
            }
        }
    }

    return block;
}


// ----------------------------------------------------
// IMPORTANT:
// acestea sunt obiecte constexpr SEPARATE.
// Astfel nu avem o singură evaluare gigantică.
// ----------------------------------------------------

template <std::size_t B>
struct SieveBlock {
    inline static constexpr Block value = makeBlock<B>();
};


// Facem doar un array mic de pointeri către blocuri.

template <std::size_t... Is>
constexpr auto makeBlocks(std::index_sequence<Is...>)
{
    return std::array<const Block*, sizeof...(Is)>{
        &SieveBlock<Is>::value...
    };
}

inline constexpr auto BLOCKS =
    makeBlocks(std::make_index_sequence<BLOCK_COUNT>{});


// ----------------------------------------------------

int countPrimes(int n)
{
    if (n < 2) {
        return 0;
    }

    // 2 este singurul prim par.
    int answer = 1;

    const std::size_t oddCount =
        static_cast<std::size_t>((n + 1) / 2);

    const std::size_t fullBlocks =
        oddCount / ODDS_PER_BLOCK;

    const std::size_t partial =
        oddCount % ODDS_PER_BLOCK;


    // Blocuri complete.
    for (std::size_t b = 0; b < fullBlocks; ++b) {

        const auto& block = *BLOCKS[b];

        for (std::uint64_t word : block.bits) {
            answer += std::popcount(word);
        }
    }


    // Ultimul bloc parțial.
    if (partial != 0) {

        const auto& block = *BLOCKS[fullBlocks];

        const std::size_t fullWords =
            partial / 64;

        const unsigned remaining =
            partial % 64;


        for (std::size_t i = 0; i < fullWords; ++i) {
            answer += std::popcount(block.bits[i]);
        }


        if (remaining != 0) {

            const std::uint64_t mask =
                (1ULL << remaining) - 1;

            answer += std::popcount(
                block.bits[fullWords] & mask
            );
        }
    }

    return answer;
}


// ----------------------------------------------------

int main()
{
    std::freopen("ciur.in", "r", stdin);
    std::freopen("ciur.out", "w", stdout);

    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::cout << countPrimes(n) << '\n';

    return 0;
}