Skip to content

dujiao_sieve.hpp

SECTIONMath INCLUDEnoya/dujiao_sieve.hpp

计算大 \(n\) 的 Möbius 或欧拉函数前缀和;用整除分块与记忆化避免筛到 \(n\)

\[ \displaystyle M(x)=\sum_{i=1}^x \mu(i),\quad \Phi(x)=\sum_{i=1}^x \varphi(i) \]

Complexity: Time: O(L) preprocessing; quotient-block memoized queries use the standard O(n^(2/3)) regime. Space: O(L + sqrt(n)) cached states.

AC 记录:sum_of_totient_function

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include

/// @complexity Time: O(L) preprocessing; quotient-block memoized queries use the standard O(n^(2/3)) regime.
/// Space: O(L + sqrt(n)) cached states.

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <unordered_map>
#include <vector>

namespace noya {

/// @brief Summatory Mobius and Euler-phi functions with a linear-sieve prefix
/// and quotient-block memoization for large arguments.
struct dujiao_sieve {
  std::vector<int> ps;
  std::vector<int> mnp;
  std::vector<std::int64_t> smu;
  std::vector<__int128> sph;
  std::unordered_map<std::uint64_t, std::int64_t> cmu;
  std::unordered_map<std::uint64_t, __int128> cph;

  explicit dujiao_sieve(int lim) { prepare(lim); }

  /// @brief Rebuild the direct prefix tables through limit.
  void prepare(int lim) {
    assert(lim >= 0);
    ps.clear();
    mnp.assign(lim + 1, 0);
    std::vector<std::int64_t> mu(lim + 1);
    std::vector<std::uint64_t> phi(lim + 1);
    if (lim >= 1) {
      mu[1] = 1;
      phi[1] = 1;
    }
    for (int val = 2; val <= lim; val++) {
      if (mnp[val] == 0) {
        mnp[val] = val;
        ps.push_back(val);
        mu[val] = -1;
        phi[val] = val - 1;
      }
      for (int p : ps) {
        if (p > mnp[val] || std::uint64_t(val) * p > std::uint64_t(lim)) {
          break;
        }
        int prd = val * p;
        mnp[prd] = p;
        if (val % p == 0) {
          mu[prd] = 0;
          phi[prd] = phi[val] * p;
        } else {
          mu[prd] = -mu[val];
          phi[prd] = phi[val] * (p - 1);
        }
      }
    }
    smu.assign(lim + 1, 0);
    sph.assign(lim + 1, 0);
    for (int val = 1; val <= lim; val++) {
      smu[val] = smu[val - 1] + mu[val];
      sph[val] = sph[val - 1] + phi[val];
    }
    cmu.clear();
    cph.clear();
  }

  /// @brief Return sum_{i=1}^n mu(i).
  std::int64_t summatory_mobius(std::uint64_t n) {
    if (n < smu.size()) {
      return smu[n];
    }
    if (auto it = cmu.find(n); it != cmu.end()) {
      return it->second;
    }
    std::int64_t res = 1;
    for (std::uint64_t l = 2, r; l <= n; l = r + 1) {
      std::uint64_t quo = n / l;
      r = n / quo;
      res -= std::int64_t(r - l + 1) * summatory_mobius(quo);
    }
    return cmu.emplace(n, res).first->second;
  }

  /// @brief Return sum_{i=1}^n phi(i) as a signed 128-bit integer.
  __int128 summatory_phi(std::uint64_t n) {
    if (n < sph.size()) {
      return sph[n];
    }
    if (auto it = cph.find(n); it != cph.end()) {
      return it->second;
    }
    __int128 res = __int128(n) * (n + 1) / 2;
    for (std::uint64_t l = 2, r; l <= n; l = r + 1) {
      std::uint64_t quo = n / l;
      r = n / quo;
      res -= __int128(r - l + 1) * summatory_phi(quo);
    }
    return cph.emplace(n, res).first->second;
  }
};

} // namespace noya
#ifndef NOYA_DUJIAO_SIEVE_HPP
#define NOYA_DUJIAO_SIEVE_HPP 1

/// @complexity Time: O(L) preprocessing; quotient-block memoized queries use the standard O(n^(2/3)) regime.
/// Space: O(L + sqrt(n)) cached states.

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <unordered_map>
#include <vector>

namespace noya {

/// @brief Summatory Mobius and Euler-phi functions with a linear-sieve prefix
/// and quotient-block memoization for large arguments.
struct dujiao_sieve {
  std::vector<int> ps;
  std::vector<int> mnp;
  std::vector<std::int64_t> smu;
  std::vector<__int128> sph;
  std::unordered_map<std::uint64_t, std::int64_t> cmu;
  std::unordered_map<std::uint64_t, __int128> cph;

  explicit dujiao_sieve(int lim) { prepare(lim); }

  /// @brief Rebuild the direct prefix tables through limit.
  void prepare(int lim) {
    assert(lim >= 0);
    ps.clear();
    mnp.assign(lim + 1, 0);
    std::vector<std::int64_t> mu(lim + 1);
    std::vector<std::uint64_t> phi(lim + 1);
    if (lim >= 1) {
      mu[1] = 1;
      phi[1] = 1;
    }
    for (int val = 2; val <= lim; val++) {
      if (mnp[val] == 0) {
        mnp[val] = val;
        ps.push_back(val);
        mu[val] = -1;
        phi[val] = val - 1;
      }
      for (int p : ps) {
        if (p > mnp[val] || std::uint64_t(val) * p > std::uint64_t(lim)) {
          break;
        }
        int prd = val * p;
        mnp[prd] = p;
        if (val % p == 0) {
          mu[prd] = 0;
          phi[prd] = phi[val] * p;
        } else {
          mu[prd] = -mu[val];
          phi[prd] = phi[val] * (p - 1);
        }
      }
    }
    smu.assign(lim + 1, 0);
    sph.assign(lim + 1, 0);
    for (int val = 1; val <= lim; val++) {
      smu[val] = smu[val - 1] + mu[val];
      sph[val] = sph[val - 1] + phi[val];
    }
    cmu.clear();
    cph.clear();
  }

  /// @brief Return sum_{i=1}^n mu(i).
  std::int64_t summatory_mobius(std::uint64_t n) {
    if (n < smu.size()) {
      return smu[n];
    }
    if (auto it = cmu.find(n); it != cmu.end()) {
      return it->second;
    }
    std::int64_t res = 1;
    for (std::uint64_t l = 2, r; l <= n; l = r + 1) {
      std::uint64_t quo = n / l;
      r = n / quo;
      res -= std::int64_t(r - l + 1) * summatory_mobius(quo);
    }
    return cmu.emplace(n, res).first->second;
  }

  /// @brief Return sum_{i=1}^n phi(i) as a signed 128-bit integer.
  __int128 summatory_phi(std::uint64_t n) {
    if (n < sph.size()) {
      return sph[n];
    }
    if (auto it = cph.find(n); it != cph.end()) {
      return it->second;
    }
    __int128 res = __int128(n) * (n + 1) / 2;
    for (std::uint64_t l = 2, r; l <= n; l = r + 1) {
      std::uint64_t quo = n / l;
      r = n / quo;
      res -= __int128(r - l + 1) * summatory_phi(quo);
    }
    return cph.emplace(n, res).first->second;
  }
};

} // namespace noya

#endif // NOYA_DUJIAO_SIEVE_HPP
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <unordered_map>
#include <vector>

/// @complexity Time: O(L) preprocessing; quotient-block memoized queries use the standard O(n^(2/3)) regime.
/// Space: O(L + sqrt(n)) cached states.

namespace noya {

/// @brief Summatory Mobius and Euler-phi functions with a linear-sieve prefix
/// and quotient-block memoization for large arguments.
struct dujiao_sieve {
  std::vector<int> ps;
  std::vector<int> mnp;
  std::vector<std::int64_t> smu;
  std::vector<__int128> sph;
  std::unordered_map<std::uint64_t, std::int64_t> cmu;
  std::unordered_map<std::uint64_t, __int128> cph;

  explicit dujiao_sieve(int lim) { prepare(lim); }

  /// @brief Rebuild the direct prefix tables through limit.
  void prepare(int lim) {
    assert(lim >= 0);
    ps.clear();
    mnp.assign(lim + 1, 0);
    std::vector<std::int64_t> mu(lim + 1);
    std::vector<std::uint64_t> phi(lim + 1);
    if (lim >= 1) {
      mu[1] = 1;
      phi[1] = 1;
    }
    for (int val = 2; val <= lim; val++) {
      if (mnp[val] == 0) {
        mnp[val] = val;
        ps.push_back(val);
        mu[val] = -1;
        phi[val] = val - 1;
      }
      for (int p : ps) {
        if (p > mnp[val] || std::uint64_t(val) * p > std::uint64_t(lim)) {
          break;
        }
        int prd = val * p;
        mnp[prd] = p;
        if (val % p == 0) {
          mu[prd] = 0;
          phi[prd] = phi[val] * p;
        } else {
          mu[prd] = -mu[val];
          phi[prd] = phi[val] * (p - 1);
        }
      }
    }
    smu.assign(lim + 1, 0);
    sph.assign(lim + 1, 0);
    for (int val = 1; val <= lim; val++) {
      smu[val] = smu[val - 1] + mu[val];
      sph[val] = sph[val - 1] + phi[val];
    }
    cmu.clear();
    cph.clear();
  }

  /// @brief Return sum_{i=1}^n mu(i).
  std::int64_t summatory_mobius(std::uint64_t n) {
    if (n < smu.size()) {
      return smu[n];
    }
    if (auto it = cmu.find(n); it != cmu.end()) {
      return it->second;
    }
    std::int64_t res = 1;
    for (std::uint64_t l = 2, r; l <= n; l = r + 1) {
      std::uint64_t quo = n / l;
      r = n / quo;
      res -= std::int64_t(r - l + 1) * summatory_mobius(quo);
    }
    return cmu.emplace(n, res).first->second;
  }

  /// @brief Return sum_{i=1}^n phi(i) as a signed 128-bit integer.
  __int128 summatory_phi(std::uint64_t n) {
    if (n < sph.size()) {
      return sph[n];
    }
    if (auto it = cph.find(n); it != cph.end()) {
      return it->second;
    }
    __int128 res = __int128(n) * (n + 1) / 2;
    for (std::uint64_t l = 2, r; l <= n; l = r + 1) {
      std::uint64_t quo = n / l;
      r = n / quo;
      res -= __int128(r - l + 1) * summatory_phi(quo);
    }
    return cph.emplace(n, res).first->second;
  }
};

} // namespace noya