squarefree_count.hpp¶
计算不超过 \(n\) 的无平方因子整数个数,适合 \(n\) 大到不能直接筛。
\[
\displaystyle #\{n\le N:\mu(n)^2=1\}
\]
Complexity: Time: O(n^(2/5) log log n). Space: O(n^(2/5)).
AC 记录:counting_squarefrees。
Implementation¶
当前头文件,省略 include guard;依赖见 #include。
/// @complexity Time: O(n^(2/5) log log n).
/// Space: O(n^(2/5)).
#include <cassert>
#include <cmath>
#include <cstdint>
#include <algorithm>
#include <limits>
#include <vector>
namespace noya {
namespace squarefree_count_detail {
inline std::uint64_t floor_square_root(std::uint64_t val) {
std::uint64_t rt = std::sqrt(static_cast<long double>(val));
while ((rt + 1) <= val / (rt + 1)) {
rt++;
}
while (rt > 0 && rt > val / rt) {
rt--;
}
return rt;
}
inline std::uint64_t floor_fifth_root(std::uint64_t val) {
auto am = [&](std::uint64_t rt) {
unsigned __int128 prd = 1;
for (int exp = 0; exp < 5; exp++) {
prd *= rt;
if (prd > val) {
return false;
}
}
return true;
};
std::uint64_t low = 0;
std::uint64_t hig = 65536;
while (hig - low > 1) {
std::uint64_t mid = (low + hig) / 2;
(am(mid) ? low : hig) = mid;
}
return low;
}
inline std::vector<std::int8_t> mobius_table(int lmt) {
std::vector<int> mnp(lmt + 1);
std::vector<int> ps;
std::vector<std::int8_t> mu(lmt + 1);
if (lmt >= 1) {
mu[1] = 1;
}
for (int val = 2; val <= lmt; val++) {
if (mnp[val] == 0) {
mnp[val] = val;
ps.push_back(val);
mu[val] = -1;
}
for (int p : ps) {
if (p > mnp[val] || val > lmt / p) {
break;
}
mnp[val * p] = p;
mu[val * p] = val % p == 0 ? 0 : std::int8_t(-mu[val]);
}
}
return mu;
}
} // namespace squarefree_count_detail
/// @brief Count square-free positive integers at most n. Mobius inversion
/// handles divisors up to `D = sqrt(n / n^(1/5))` directly. The remaining
/// terms share only `O(n^(1/5))` distinct square-root quotients; their Mertens
/// values are recovered in descending order with the quotient-block
/// recurrence, reducing both time and memory from O(sqrt(n)) to O(n^(2/5)).
inline std::uint64_t squarefree_count(std::uint64_t n) {
if (n == 0) {
return 0;
}
std::uint64_t spl =
std::max<std::uint64_t>(1, squarefree_count_detail::floor_fifth_root(n));
std::uint64_t lim = squarefree_count_detail::floor_square_root(n / spl);
assert(lim <= std::uint64_t(std::numeric_limits<int>::max()));
auto mu = squarefree_count_detail::mobius_table(int(lim));
std::vector<std::int64_t> mer(lim + 1);
for (std::uint64_t val = 1; val <= lim; val++) {
mer[val] = mer[val - 1] + mu[val];
}
__int128 sm = 0;
for (std::uint64_t val = 1; val <= lim; val++) {
sm += __int128(mu[val]) * (n / val / val);
}
std::vector<std::int64_t> lmu;
lmu.reserve(spl > 0 ? spl - 1 : 0);
__int128 sum = 0;
for (std::uint64_t idx = spl; idx-- > 1;) {
std::uint64_t arg = squarefree_count_detail::floor_square_root(n / idx);
std::uint64_t sr = squarefree_count_detail::floor_square_root(arg);
std::int64_t val = 1;
std::uint64_t qb = arg / (sr + 1);
for (std::uint64_t div = 1; div <= qb; div++) {
val -= std::int64_t(arg / div - arg / (div + 1)) * mer[div];
}
for (std::uint64_t div = 2; div <= sr; div++) {
std::uint64_t quo = arg / div;
if (quo <= lim) {
val -= mer[quo];
} else {
std::uint64_t ri = idx * div * div;
assert(ri < spl);
val -= lmu[spl - ri - 1];
}
}
lmu.push_back(val);
sum += val;
}
__int128 res = sm + sum - __int128(spl - 1) * mer[lim];
assert(res >= 0);
return std::uint64_t(res);
}
} // namespace noya
#ifndef NOYA_SQUAREFREE_COUNT_HPP
#define NOYA_SQUAREFREE_COUNT_HPP 1
/// @complexity Time: O(n^(2/5) log log n).
/// Space: O(n^(2/5)).
#include <cassert>
#include <cmath>
#include <cstdint>
#include <algorithm>
#include <limits>
#include <vector>
namespace noya {
namespace squarefree_count_detail {
inline std::uint64_t floor_square_root(std::uint64_t val) {
std::uint64_t rt = std::sqrt(static_cast<long double>(val));
while ((rt + 1) <= val / (rt + 1)) {
rt++;
}
while (rt > 0 && rt > val / rt) {
rt--;
}
return rt;
}
inline std::uint64_t floor_fifth_root(std::uint64_t val) {
auto am = [&](std::uint64_t rt) {
unsigned __int128 prd = 1;
for (int exp = 0; exp < 5; exp++) {
prd *= rt;
if (prd > val) {
return false;
}
}
return true;
};
std::uint64_t low = 0;
std::uint64_t hig = 65536;
while (hig - low > 1) {
std::uint64_t mid = (low + hig) / 2;
(am(mid) ? low : hig) = mid;
}
return low;
}
inline std::vector<std::int8_t> mobius_table(int lmt) {
std::vector<int> mnp(lmt + 1);
std::vector<int> ps;
std::vector<std::int8_t> mu(lmt + 1);
if (lmt >= 1) {
mu[1] = 1;
}
for (int val = 2; val <= lmt; val++) {
if (mnp[val] == 0) {
mnp[val] = val;
ps.push_back(val);
mu[val] = -1;
}
for (int p : ps) {
if (p > mnp[val] || val > lmt / p) {
break;
}
mnp[val * p] = p;
mu[val * p] = val % p == 0 ? 0 : std::int8_t(-mu[val]);
}
}
return mu;
}
} // namespace squarefree_count_detail
/// @brief Count square-free positive integers at most n. Mobius inversion
/// handles divisors up to `D = sqrt(n / n^(1/5))` directly. The remaining
/// terms share only `O(n^(1/5))` distinct square-root quotients; their Mertens
/// values are recovered in descending order with the quotient-block
/// recurrence, reducing both time and memory from O(sqrt(n)) to O(n^(2/5)).
inline std::uint64_t squarefree_count(std::uint64_t n) {
if (n == 0) {
return 0;
}
std::uint64_t spl =
std::max<std::uint64_t>(1, squarefree_count_detail::floor_fifth_root(n));
std::uint64_t lim = squarefree_count_detail::floor_square_root(n / spl);
assert(lim <= std::uint64_t(std::numeric_limits<int>::max()));
auto mu = squarefree_count_detail::mobius_table(int(lim));
std::vector<std::int64_t> mer(lim + 1);
for (std::uint64_t val = 1; val <= lim; val++) {
mer[val] = mer[val - 1] + mu[val];
}
__int128 sm = 0;
for (std::uint64_t val = 1; val <= lim; val++) {
sm += __int128(mu[val]) * (n / val / val);
}
std::vector<std::int64_t> lmu;
lmu.reserve(spl > 0 ? spl - 1 : 0);
__int128 sum = 0;
for (std::uint64_t idx = spl; idx-- > 1;) {
std::uint64_t arg = squarefree_count_detail::floor_square_root(n / idx);
std::uint64_t sr = squarefree_count_detail::floor_square_root(arg);
std::int64_t val = 1;
std::uint64_t qb = arg / (sr + 1);
for (std::uint64_t div = 1; div <= qb; div++) {
val -= std::int64_t(arg / div - arg / (div + 1)) * mer[div];
}
for (std::uint64_t div = 2; div <= sr; div++) {
std::uint64_t quo = arg / div;
if (quo <= lim) {
val -= mer[quo];
} else {
std::uint64_t ri = idx * div * div;
assert(ri < spl);
val -= lmu[spl - ri - 1];
}
}
lmu.push_back(val);
sum += val;
}
__int128 res = sm + sum - __int128(spl - 1) * mer[lim];
assert(res >= 0);
return std::uint64_t(res);
}
} // namespace noya
#endif // NOYA_SQUAREFREE_COUNT_HPP
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <limits>
#include <vector>
/// @complexity Time: O(n^(2/5) log log n).
/// Space: O(n^(2/5)).
namespace noya {
namespace squarefree_count_detail {
inline std::uint64_t floor_square_root(std::uint64_t val) {
std::uint64_t rt = std::sqrt(static_cast<long double>(val));
while ((rt + 1) <= val / (rt + 1)) {
rt++;
}
while (rt > 0 && rt > val / rt) {
rt--;
}
return rt;
}
inline std::uint64_t floor_fifth_root(std::uint64_t val) {
auto am = [&](std::uint64_t rt) {
unsigned __int128 prd = 1;
for (int exp = 0; exp < 5; exp++) {
prd *= rt;
if (prd > val) {
return false;
}
}
return true;
};
std::uint64_t low = 0;
std::uint64_t hig = 65536;
while (hig - low > 1) {
std::uint64_t mid = (low + hig) / 2;
(am(mid) ? low : hig) = mid;
}
return low;
}
inline std::vector<std::int8_t> mobius_table(int lmt) {
std::vector<int> mnp(lmt + 1);
std::vector<int> ps;
std::vector<std::int8_t> mu(lmt + 1);
if (lmt >= 1) {
mu[1] = 1;
}
for (int val = 2; val <= lmt; val++) {
if (mnp[val] == 0) {
mnp[val] = val;
ps.push_back(val);
mu[val] = -1;
}
for (int p : ps) {
if (p > mnp[val] || val > lmt / p) {
break;
}
mnp[val * p] = p;
mu[val * p] = val % p == 0 ? 0 : std::int8_t(-mu[val]);
}
}
return mu;
}
} // namespace squarefree_count_detail
/// @brief Count square-free positive integers at most n. Mobius inversion
/// handles divisors up to `D = sqrt(n / n^(1/5))` directly. The remaining
/// terms share only `O(n^(1/5))` distinct square-root quotients; their Mertens
/// values are recovered in descending order with the quotient-block
/// recurrence, reducing both time and memory from O(sqrt(n)) to O(n^(2/5)).
inline std::uint64_t squarefree_count(std::uint64_t n) {
if (n == 0) {
return 0;
}
std::uint64_t spl =
std::max<std::uint64_t>(1, squarefree_count_detail::floor_fifth_root(n));
std::uint64_t lim = squarefree_count_detail::floor_square_root(n / spl);
assert(lim <= std::uint64_t(std::numeric_limits<int>::max()));
auto mu = squarefree_count_detail::mobius_table(int(lim));
std::vector<std::int64_t> mer(lim + 1);
for (std::uint64_t val = 1; val <= lim; val++) {
mer[val] = mer[val - 1] + mu[val];
}
__int128 sm = 0;
for (std::uint64_t val = 1; val <= lim; val++) {
sm += __int128(mu[val]) * (n / val / val);
}
std::vector<std::int64_t> lmu;
lmu.reserve(spl > 0 ? spl - 1 : 0);
__int128 sum = 0;
for (std::uint64_t idx = spl; idx-- > 1;) {
std::uint64_t arg = squarefree_count_detail::floor_square_root(n / idx);
std::uint64_t sr = squarefree_count_detail::floor_square_root(arg);
std::int64_t val = 1;
std::uint64_t qb = arg / (sr + 1);
for (std::uint64_t div = 1; div <= qb; div++) {
val -= std::int64_t(arg / div - arg / (div + 1)) * mer[div];
}
for (std::uint64_t div = 2; div <= sr; div++) {
std::uint64_t quo = arg / div;
if (quo <= lim) {
val -= mer[quo];
} else {
std::uint64_t ri = idx * div * div;
assert(ri < spl);
val -= lmu[spl - ri - 1];
}
}
lmu.push_back(val);
sum += val;
}
__int128 res = sm + sum - __int128(spl - 1) * mer[lim];
assert(res >= 0);
return std::uint64_t(res);
}
} // namespace noya