競技プログラミング用 C++ ライブラリ
math/multiplicative_prefix_sum.hpp · GitHub で見る
共通ページ
乗法的関数 \(f\) の \(\displaystyle F(x)=\sum_{i=1}^x f(i)\) を計算します。f(1) = 1 とし、互いに素な \(a,b\) について \(f(ab)=f(a)f(b)\) を満たす関数が対象です。上限を含み、F(0) = 0 です。
例えば、オイラーのトーティエント関数の和は次のように求められます。
#include "lib/math/multiplicative_prefix_sum.hpp"
#include "lib/math/mint.hpp"
MultiplicativePrefixSum<mint> sieve(100);
auto prime_prefix = sieve.prime_sum_linear(mint(-1), mint(1));
auto phi = [](std::uint64_t p, unsigned, std::uint64_t pe) {
return mint(pe - pe / p);
};
auto answer = sieve.sum(prime_prefix, phi); // 3044
auto prefix = sieve.prefix_sum(prime_prefix, phi);
auto at10 = sieve.get(prefix, 10); // 32
利用には、素数上の prefix sum の表 prime_prefix と、素数冪に対する値を返す関数 prime_power が必要です。
prime_prefix は長さ sieve.size() の std::vector<T> です。q = sieve.quotients() とすると、各要素に次の値を入れます。
quotients()[i] 以下の素数だけについて、f(p) を合計した値です。1 や合成数の寄与は含めません。添字 i は quotients() の添字で、素数の番号や整数値そのものではありません。全体の累積和への復元は prefix_sum が行い、f(1) = 1 と高い素数冪・異なる素数の積の寄与を加えます。
例えば n = 10 で、冒頭と同じトーティエント関数を使う場合は、素数上の値が \(f(p)=p-1\) なので、渡す表は次のようになります。
i |
quotients()[i] |
合計する値 | prime_prefix[i] |
|---|---|---|---|
| 0 | 10 | (2-1)+(3-1)+(5-1)+(7-1) |
13 |
| 1 | 5 | (2-1)+(3-1)+(5-1) |
7 |
| 2 | 3 | (2-1)+(3-1) |
3 |
| 3 | 2 | 2-1 |
1 |
| 4 | 1 | 素数なし | 0 |
つまり、この場合の引数は std::vector<T>{13, 7, 3, 1, 0} です。prime_power(p,e,pe) に T(pe-pe/p) を返す関数を渡すと、prefix_sum の返す表は {32, 10, 4, 2, 1} になります。
素数上の値が \(f(p)=a+bp\) なら、sieve.prime_sum_linear(a,b) の戻り値をそのまま渡せます。最初のトーティエントの例では \(f(p)=p-1\) なので、prime_sum_linear(T(-1),T(1)) で作ります。他の形では prime_sum で作った表の線形結合や、自分で用意した正しい表を渡せます。同じ上限 n の quotients() に合わせた表を使ってください。
以下では \(q_i=\texttt{quotients()}[i]\) とします。
| 操作 | 内容・返り値 |
|---|---|
MultiplicativePrefixSum<T>(n = 0) |
上限 n の計算領域を作る |
max_n() / size()
|
上限/商の表の要素数 |
quotients() |
異なる正の n/i を降順に並べた const vector<uint64_t>&
|
index(x) |
quotients() に含まれる正の x の添字 |
prime_sum_linear(a,b) |
各要素が \(\displaystyle\sum_{p\leq q_i,\ p\text{ prime}}(a+bp)\) の vector<T> を返す |
prime_sum(integer_prefix) |
入力: \(\displaystyle\texttt{integer\_prefix}(x)=\sum_{k=1}^x g(k)\) を T で返す関数。\(g(1)=1\) かつ全ての正整数 \(a,b\) に対して \(g(ab)=g(a)g(b)\) を満たす完全乗法的な \(g\) に限る。返り値: 各要素が \(\displaystyle\sum_{p\leq q_i,\ p\text{ prime}}g(p)\) の vector<T>
|
sum(prime_prefix,prime_power) |
F(n) を T で返す |
prefix_sum(prime_prefix,prime_power) |
各要素が \(\displaystyle F(q_i)=\sum_{k=1}^{q_i}f(k)\) の vector<T> を返す |
get(table,x) |
作った表の x に対する値。x = 0 は 0 |
swap(other) |
二つの計算領域を交換する |
prime_power(p,e,pe) は \(f(p^e)\) を T で返します。p は素数、e >= 1、pe = p^e <= n です。同じ引数には同じ値を返してください。prime_prefix の素数上の値と、このコールバックの e = 1 の値は一致する必要があります。高い冪に対する値は任意に指定できます。
T は整数・modint など、0・1 の構築と加減乗算を備えた可換環の値を使います。コールバックの戻り値も T にしてください。逆元や除算は不要で、合成数 mod の型も使えます。prime_sum_linear では std::uint64_t から T への変換も使います。三角数は整数の片側を先に半分にするため、2 の逆元を要求しません。整数型の場合は、中間値もその型に収めてください。大きな整数和には __int128_t などを使えます。
代表的な指定は次の通りです。
| 関数 | 素数上の表 | prime_power(p,e,pe) |
|---|---|---|
| 常に 1 | prime_sum_linear(T(1),T(0)) |
T(1) |
| 恒等関数 | prime_sum_linear(T(0),T(1)) |
T(pe) |
| トーティエント φ | prime_sum_linear(T(-1),T(1)) |
T(pe-pe/p) |
| メビウス μ | prime_sum_linear(T(-1),T(0)) |
e == 1 ? T(-1) : T(0) |
| 約数個数 τ | prime_sum_linear(T(2),T(0)) |
T(e+1) |
| 約数和 σ | prime_sum_linear(T(1),T(1)) |
\(\displaystyle\sum_{j=0}^{e}T(p)^j\) |
| 平方数なら 1 | prime_sum_linear(T(0),T(0)) |
T(e%2 == 0) |
prime_sum(integer_prefix) では、補助関数 \(g\) が完全乗法的、すなわち互いに素という制約なしに \(g(ab)=g(a)g(b)\)、かつ \(g(1)=1\) であることが必要です。通常の乗法性だけでは不十分で、例えばトーティエント関数の累積和を直接渡すことはできません。integer_prefix(x) に \(\displaystyle\sum_{i=1}^x g(i)\) を与えると、各点で \(\displaystyle\sum_{p\leq x,\ p\text{ prime}}g(p)\) を返します。g(i)=1,i,i^2 や Dirichlet 指標などが使えます。返された表の線形結合を作れば、素数上の値が多項式などで表せる関数にも対応できます。別の方法で作った正しい素数上の表を sum・prefix_sum に渡しても構いません。
n は非負の std::uint64_t です。全ての表は quotients() と同じ長さ・順序です。get・index の正の引数はこの列に含まれる値だけに限ります。特に 1〜整数平方根までは全て含まれます。prefix_sum は長さ n の配列を作る操作ではありません。任意の上限 m の和は、上限 m のオブジェクトを構築して sum で求められます。
上限 0 では表は空で、sum と get(table,0) は 0 です。上限 1 では sum は 1 です。呼び出しはオブジェクトや入力の表を変更しないため、別の関数や異なる素数冪の値で計算領域を再利用できます。コピーは独立し、ムーブ後の元は上限 0 の状態になります。必要な領域は入力に応じて増えるため、時間とメモリに収まる範囲で使ってください。
商の表と素数篩を共有し、素数上の表は奇数に圧縮した Min_25 篩で作ります。prefix_sum は逆順の素数から高い冪を復元し、商が同じになる範囲をまとめて更新します。
sum は Black algorithm の列挙を使います。上限が \(2^{32}\) 以上で、T::mod() が 32 bit の法を返し、3 の逆元が存在する modint では、prime_sum・prime_sum_linear・sum を素数の範囲ごとに分割する処理へ自動で切り替えます。sum では平方因子を持たない数の和と強力数上の補正を組み合わせます。この処理は Library Checker の高速な提出 を参考にしています。型への追加要件はなく、それ以外の型でも同じ API を使えます。
T の演算とコールバックを \(O(1)\) とすると、構築は \(\displaystyle O(\sqrt n\log\log n)\) 時間・\(\displaystyle O(\sqrt n)\) 空間です。prime_sum・prime_sum_linear・sum・prefix_sum はそれぞれ \(\displaystyle O\!\left(\frac{n^{3/4}}{\log n}\right)\) 時間、保持する表と作業領域は \(\displaystyle O(\sqrt n)\) です。表の参照は \(O(1)\)、コピーは \(\displaystyle O(\sqrt n)\) 時間・空間です。n <= 1 は定数時間で処理します。
#ifndef MULTIPLICATIVE_PREFIX_SUM_HPP
#define MULTIPLICATIVE_PREFIX_SUM_HPP
#include "../components/floor_quotients.hpp"
#include "../components/multiplicative_sum_backend.hpp"
#include "prime_sieve.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <vector>
template<class T> class MultiplicativePrefixSum {
FloorQuotients quotients_;
std::vector<std::size_t> primes_;
bool use_backend(T& inverse) const {
if constexpr (requires { std::uint64_t(T::mod()); T(0) == T(0); }) {
const auto modulus = std::uint64_t(T::mod());
if (max_n() >= (UINT64_C(1) << 32) && modulus > 1 && modulus <= UINT32_MAX && modulus % 3) {
inverse = T((modulus * (modulus % 3 == 1 ? 2 : 1) + 1) / 3);
return T(3) * inverse == T(1);
}
}
return false;
}
template<class R, class Prefix> std::vector<R> prime_sum_impl(Prefix&& integer_prefix) const {
const std::uint64_t n = max_n(), root = quotients_.root();
const std::size_t large = quotients_.large_size(), length = size(), half = (root + 1) / 2;
const R two = n >= 2 ? integer_prefix(2) - integer_prefix(1) : R(0);
std::vector<R> result, small;
result.reserve(length), small.reserve(half);
auto initial = [&](std::uint64_t x) { return x == 1 ? R(0) : integer_prefix(x) - two * integer_prefix(x / 2) - R(1); };
for (std::size_t i = 0; i < large; ++i) result.push_back(initial(quotients()[i]));
for (std::size_t i = 0; i < half; ++i) small.push_back(initial(2 * i + 1));
for (std::uint64_t p : primes_) {
if (p == 2) continue;
const R weight = integer_prefix(p) - integer_prefix(p - 1), before = small[(p - 2) / 2];
const auto end = std::min<std::uint64_t>(large, n / (p * p));
const auto split = std::min<std::uint64_t>(end, large / p);
std::uint64_t i = 1;
for (; i <= split; ++i) result[i - 1] -= weight * (result[i * p - 1] - before);
for (; i <= end; ++i) result[i - 1] -= weight * (small[(n / (i * p) - 1) / 2] - before);
std::size_t x = half;
for (std::uint64_t q = (root / p - 1) | 1; q >= p; q -= 2) {
const R delta = weight * (small[(q - 1) / 2] - before);
for (const auto stop = (q * p + 1) / 2; x >= stop; --x) small[x - 1] -= delta;
}
}
for (auto& value : result) value += two;
for (auto x = root; x; --x) result.push_back(small[(x - 1) / 2] + (x >= 2 ? two : R(0)));
return result;
}
public:
explicit MultiplicativePrefixSum(std::uint64_t n = 0) : quotients_(n), primes_(prime_sieve(quotients_.root() + 1)) {}
MultiplicativePrefixSum(const MultiplicativePrefixSum&) = default;
MultiplicativePrefixSum& operator=(const MultiplicativePrefixSum&) = default;
MultiplicativePrefixSum(MultiplicativePrefixSum&& other) noexcept : MultiplicativePrefixSum() { swap(other); }
MultiplicativePrefixSum& operator=(MultiplicativePrefixSum&& other) noexcept {
if (this != &other) { MultiplicativePrefixSum empty; swap(empty); swap(other); }
return *this;
}
void swap(MultiplicativePrefixSum& other) noexcept { quotients_.swap(other.quotients_); primes_.swap(other.primes_); }
std::uint64_t max_n() const { return quotients_.max_n(); }
std::size_t size() const { return quotients_.size(); }
const std::vector<std::uint64_t>& quotients() const { return quotients_.values(); }
std::size_t index(std::uint64_t x) const { return quotients_.index(x); }
T get(const std::vector<T>& table, std::uint64_t x) const {
assert(table.size() == size());
return x ? table[index(x)] : T(0);
}
template<class Prefix> std::vector<T> prime_sum(Prefix integer_prefix) const {
T inverse(0);
if (use_backend(inverse)) return multiplicative_sum_detail::Backend<T>(quotients_, primes_).prime_sum(integer_prefix, inverse);
return prime_sum_impl<T>(integer_prefix);
}
std::vector<T> prime_sum_linear(T constant, T linear) const {
auto identity_prefix = [](std::uint64_t x) -> T {
return x & 1 ? T(x) * (T(x / 2) + T(1)) : T(x / 2) * (T(x) + T(1));
};
T inverse(0);
const bool split = use_backend(inverse);
if constexpr (requires { linear == T(0); }) {
if (linear == T(0)) {
if (constant == T(0)) return std::vector<T>(size(), T(0));
if (split) {
auto prefix = [](std::uint64_t x) { return T(x); };
auto result = multiplicative_sum_detail::Backend<T>(quotients_, primes_).prime_sum(prefix, inverse);
for (auto& value : result) value *= constant;
return result;
}
auto counts = prime_sum_impl<std::uint64_t>([](std::uint64_t x) { return x; });
std::vector<T> result;
result.reserve(size());
for (auto value : counts) result.push_back(T(value) * constant);
return result;
}
if (constant == T(0)) {
auto result = prime_sum(identity_prefix);
for (auto& value : result) value *= linear;
return result;
}
}
if (split) {
using Weights = multiplicative_sum_detail::LinearWeights<T>;
auto prefix = [&](std::uint64_t x) { return Weights(T(x), identity_prefix(x)); };
auto sums = multiplicative_sum_detail::Backend<Weights>(quotients_, primes_).prime_sum(prefix, Weights(inverse));
std::vector<T> result;
result.reserve(size());
for (const auto& value : sums) result.push_back(value.count * constant + value.weighted * linear);
return result;
}
const std::uint64_t n = max_n(), root = quotients_.root();
const std::size_t large = quotients_.large_size(), length = size(), half = (root + 1) / 2;
std::vector<T> result, small;
std::vector<std::uint64_t> count;
std::vector<std::uint32_t> count_small;
result.reserve(length), small.reserve(half);
count.reserve(large), count_small.reserve(half);
for (std::size_t i = 0; i < large; ++i) {
const auto x = quotients()[i], c = x / 2 + (x & 1);
count.push_back(std::uint64_t(c - 1));
result.push_back(T(c) * T(c) - T(1));
}
for (std::size_t i = 0; i < half; ++i) {
count_small.push_back(std::uint32_t(i));
small.push_back(T(i + 1) * T(i + 1) - T(1));
}
for (std::uint64_t p : primes_) {
if (p == 2) continue;
const T weight = T(p), before = small[(p - 2) / 2];
const std::uint64_t count_before = count_small[(p - 2) / 2];
const auto end = std::min<std::uint64_t>(large, n / (p * p));
const auto split = std::min<std::uint64_t>(end, large / p);
std::uint64_t i = 1;
for (; i <= split; ++i) {
count[i - 1] -= count[i * p - 1] - count_before;
result[i - 1] -= weight * (result[i * p - 1] - before);
}
for (; i <= end; ++i) {
const auto at = (n / (i * p) - 1) / 2;
count[i - 1] -= count_small[at] - count_before;
result[i - 1] -= weight * (small[at] - before);
}
std::size_t x = half;
for (std::uint64_t q = (root / p - 1) | 1; q >= p; q -= 2) {
const std::uint64_t count_delta = count_small[(q - 1) / 2] - count_before;
const T delta = weight * (small[(q - 1) / 2] - before);
for (const auto stop = (q * p + 1) / 2; x >= stop; --x) {
count_small[x - 1] -= count_delta;
small[x - 1] -= delta;
}
}
}
for (std::size_t i = 0; i < large; ++i) result[i] = (T(count[i]) + T(1)) * constant + (result[i] + T(2)) * linear;
for (auto x = root; x; --x) result.push_back((T(count_small[(x - 1) / 2]) + T(x >= 2)) * constant + (small[(x - 1) / 2] + T(x >= 2 ? 2 : 0)) * linear);
return result;
}
template<class PrimePower> std::vector<T> prefix_sum(const std::vector<T>& prime_prefix, PrimePower prime_power) const {
assert(prime_prefix.size() == size());
std::vector<T> result = prime_prefix, weights;
std::vector<std::uint64_t> powers;
weights.reserve(64), powers.reserve(64);
for (std::size_t j = primes_.size(); j--;) {
const std::uint64_t p = primes_[j];
const T before = prime_prefix[index(p)];
weights.clear(), powers.clear();
for (std::uint64_t power = p;; power *= p) {
powers.push_back(power);
weights.push_back(prime_power(p, static_cast<unsigned>(powers.size()), power));
if (power > max_n() / p) break;
}
const T first = weights[0], second = weights[1];
const auto length = size(), large = quotients_.large_size();
const auto end = std::min<std::uint64_t>(large, max_n() / (p * p));
std::size_t i = 0;
if (powers.size() > 2) for (; i < end && quotients()[i] >= powers[2]; ++i) {
const auto d0 = (i + 1) * p;
T delta = first * (result[d0 <= large ? d0 - 1 : length - max_n() / d0] - before) + second;
for (std::size_t e = 1; e + 1 < powers.size() && powers[e + 1] <= quotients()[i]; ++e) {
const auto d = (i + 1) * powers[e];
delta += weights[e] * (result[d <= large ? d - 1 : length - max_n() / d] - before) + weights[e + 1];
}
result[i] += delta;
}
const auto split = std::min<std::uint64_t>(end, large / p);
for (; i < split; ++i)
result[i] += first * (result[(i + 1) * p - 1] - before) + second;
for (; i < end; ++i)
result[i] += first * (result[length - max_n() / ((i + 1) * p)] - before) + second;
std::uint64_t x = quotients_.root();
while (x >= p * p) {
const auto q = x / p, stop = q * p;
T delta = first * (result[length - q] - before) + second;
for (std::size_t e = 1; e + 1 < powers.size() && powers[e + 1] <= x; ++e)
delta += weights[e] * (result[length - x / powers[e]] - before) + weights[e + 1];
for (; x >= stop; --x) result[length - x] += delta;
}
}
for (auto& value : result) value += T(1);
return result;
}
template<class PrimePower> T sum(const std::vector<T>& prime_prefix, PrimePower prime_power) const {
assert(prime_prefix.size() == size());
if (!max_n()) return T(0);
T inverse(0);
if (use_backend(inverse)) return multiplicative_sum_detail::Backend<T>(quotients_, primes_).sum(prime_prefix, prime_power, inverse);
std::vector<T> first, second, square_prefix{T(0)};
first.reserve(primes_.size()), second.reserve(primes_.size()), square_prefix.reserve(primes_.size() + 1);
for (std::uint64_t p : primes_) {
first.push_back(prime_power(p, 1, p));
second.push_back(prime_power(p, 2, p * p));
square_prefix.push_back(square_prefix.back() + second.back());
}
T result = prime_prefix[0] + T(1);
const auto length = size(), large = quotients_.large_size();
auto dfs = [&](auto&& self, std::size_t i, unsigned e, std::uint64_t product, std::uint64_t power) -> T {
const std::uint64_t p = primes_[i], limit = max_n() / product;
T delta = e == 1 ? second[i] : prime_power(p, e + 1, power * p);
if (p * p <= limit) delta += self(self, i, e + 1, product * p, power * p);
T weight = e == 1 ? first[i] : e == 2 ? second[i] : prime_power(p, e, power);
if constexpr (requires { weight == T(0); }) if (weight == T(0)) return delta;
T tail = prime_prefix[product <= large ? product - 1 : length - limit] - prime_prefix[length - p];
std::size_t j = i + 1;
for (; j < primes_.size(); ++j) {
const std::uint64_t q = primes_[j];
if (__uint128_t(q) * q * q > limit) break;
tail += self(self, j, 1, product * q, q);
}
const auto begin = j;
for (; j < primes_.size(); ++j) {
const std::uint64_t q = primes_[j];
if (q * q > limit) break;
tail += first[j] * (prime_prefix[product * q <= large ? product * q - 1 : length - limit / q] - prime_prefix[length - q]);
}
return delta + weight * (tail + square_prefix[j] - square_prefix[begin]);
};
for (std::size_t i = 0; i < primes_.size(); ++i) result += dfs(dfs, i, 1, primes_[i], primes_[i]);
return result;
}
};
#endif
#line 1 "math/multiplicative_prefix_sum.hpp"
#line 1 "components/floor_quotients.hpp"
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <iterator>
#include <utility>
#include <vector>
namespace floor_quotient_detail {
inline std::uint64_t square_root(std::uint64_t n) {
std::uint64_t root = std::sqrt(static_cast<long double>(n));
while (__uint128_t(root) * root > n) --root;
while (__uint128_t(root + 1) * (root + 1) <= n) ++root;
return root;
}
inline std::uint64_t divide(std::uint64_t n, std::uint64_t divisor) {
if constexpr (std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::radix == 2 && std::numeric_limits<double>::digits >= 53) {
if (n <= (std::uint64_t(1) << 53)) {
std::uint64_t value = double(n) / double(divisor);
return value - (value * divisor > n);
}
}
return n / divisor;
}
}
class FloorQuotientRange {
std::uint64_t n_, root_, large_;
public:
explicit FloorQuotientRange(std::uint64_t n = 0) : n_(n), root_(floor_quotient_detail::square_root(n)), large_(root_ - (n < root_ * (root_ + 1))) {}
std::uint64_t max_n() const { return n_; }
std::uint64_t root() const { return root_; }
std::uint64_t large_size() const { return large_; }
std::uint64_t size() const { return root_ + large_; }
bool empty() const { return !n_; }
std::uint64_t operator[](std::uint64_t i) const {
assert(i < size());
return i < root_ ? i + 1 : floor_quotient_detail::divide(n_, size() - i);
}
class iterator {
const FloorQuotientRange *range_ = nullptr;
std::uint64_t index_ = 0;
friend class FloorQuotientRange;
iterator(const FloorQuotientRange *range, std::uint64_t i) : range_(range), index_(i) {}
public:
using value_type = std::uint64_t;
using difference_type = std::int64_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
using reference = value_type;
using pointer = void;
iterator() = default;
value_type operator*() const { return (*range_)[index_]; }
iterator &operator++() { ++index_; return *this; }
iterator operator++(int) { auto result = *this; ++*this; return result; }
bool operator==(const iterator &) const = default;
};
iterator begin() const { return iterator(this, 0); }
iterator end() const { return iterator(this, size()); }
};
class FloorQuotients {
std::uint64_t n_, root_ = 0;
std::size_t large_ = 0;
std::vector<std::uint64_t> values_;
public:
explicit FloorQuotients(std::uint64_t n = 0) : n_(n) {
if (!n) return;
root_ = floor_quotient_detail::square_root(n);
large_ = n / (root_ + 1);
values_.reserve(large_ + root_);
for (std::size_t i = 1; i <= large_; ++i) values_.push_back(floor_quotient_detail::divide(n, i));
for (auto x = root_; x; --x) values_.push_back(x);
}
FloorQuotients(const FloorQuotients&) = default;
FloorQuotients& operator=(const FloorQuotients&) = default;
FloorQuotients(FloorQuotients&& other) noexcept : FloorQuotients() { swap(other); }
FloorQuotients& operator=(FloorQuotients&& other) noexcept {
if (this != &other) { FloorQuotients empty; swap(empty); swap(other); }
return *this;
}
void swap(FloorQuotients& other) noexcept {
std::swap(n_, other.n_), std::swap(root_, other.root_), std::swap(large_, other.large_);
values_.swap(other.values_);
}
std::uint64_t max_n() const { return n_; }
std::uint64_t root() const { return root_; }
std::size_t size() const { return values_.size(); }
std::size_t large_size() const { return large_; }
const std::vector<std::uint64_t>& values() const { return values_; }
bool contains(std::uint64_t x) const { return x && x <= n_ && (x <= root_ || n_ / (n_ / x) == x); }
std::size_t index(std::uint64_t x) const {
assert(x && x <= n_);
return x <= root_ ? size() - x : n_ / x - 1;
}
std::size_t divided_index(std::size_t i, std::uint64_t divisor) const {
assert(i < size() && divisor && divisor <= values_[i]);
if (i >= large_) return size() - values_[i] / divisor;
const auto d = (i + 1) * divisor;
return d <= large_ ? d - 1 : size() - n_ / d;
}
};
#line 1 "components/multiplicative_sum_backend.hpp"
#line 6 "components/multiplicative_sum_backend.hpp"
#include <algorithm>
#line 10 "components/multiplicative_sum_backend.hpp"
namespace multiplicative_sum_detail {
template<class T> struct LinearWeights {
T count, weighted;
template<class I> explicit LinearWeights(I value) : count(T(value)), weighted(T(value)) {}
LinearWeights(T count_, T weighted_) : count(count_), weighted(weighted_) {}
LinearWeights& operator+=(const LinearWeights& b) { count += b.count; weighted += b.weighted; return *this; }
LinearWeights& operator-=(const LinearWeights& b) { count -= b.count; weighted -= b.weighted; return *this; }
friend LinearWeights operator+(LinearWeights a, const LinearWeights& b) { return a += b; }
friend LinearWeights operator-(LinearWeights a, const LinearWeights& b) { return a -= b; }
friend LinearWeights operator*(const LinearWeights& a, const LinearWeights& b) { return LinearWeights(a.count * b.count, a.weighted * b.weighted); }
};
template<class T> struct LinearPrime {
T weighted;
explicit LinearPrime(const LinearWeights<T>& value) : weighted(value.weighted) {}
explicit LinearPrime(T value) : weighted(value) {}
operator LinearWeights<T>() const { return LinearWeights<T>(T(1), weighted); }
friend LinearPrime operator*(const LinearPrime& a, const LinearPrime& b) { return LinearPrime(a.weighted * b.weighted); }
friend LinearWeights<T> operator*(const LinearPrime& a, const LinearWeights<T>& b) { return LinearWeights<T>(b.count, a.weighted * b.weighted); }
friend LinearWeights<T> operator*(const LinearWeights<T>& a, const LinearPrime& b) { return LinearWeights<T>(a.count, a.weighted * b.weighted); }
};
template<class T> struct PrimeValue { using type = T; };
template<class T> struct PrimeValue<LinearWeights<T>> { using type = LinearPrime<T>; };
template<class T> class Backend {
using U = std::uint64_t;
U n, root, large;
std::size_t length;
std::vector<U> primes;
std::vector<typename PrimeValue<T>::type> first;
std::vector<T> corrections;
std::vector<std::size_t> offsets;
static U square_root(U value) {
U result = std::sqrt(static_cast<long double>(value));
while (__uint128_t(result) * result > value) --result;
while (__uint128_t(result + 1) * (result + 1) <= value) ++result;
return result;
}
static U cube_root(U value) {
U result = std::cbrt(static_cast<long double>(value));
while (__uint128_t(result) * result * result > value) --result;
while (__uint128_t(result + 1) * (result + 1) * (result + 1) <= value) ++result;
return result;
}
static U fifth_root(U value) {
U result = std::pow(static_cast<long double>(value), 0.2L);
auto power = [](U x) { const __uint128_t square = __uint128_t(x) * x; return square * square * x; };
while (power(result) > value) --result;
while (power(result + 1) <= value) ++result;
return result;
}
std::size_t small(U x) const { return x - 1; }
std::size_t big(U d) const { return length - d; }
std::size_t pi(U x) const { return std::upper_bound(primes.begin(), primes.end() - 1, x) - primes.begin(); }
struct Tree {
std::vector<T> data;
explicit Tree(const std::vector<T>& a, std::size_t size) : data(size + 1, T(0)) {
T previous(0);
for (std::size_t i = 1; i <= size; ++i) { data[i] = a[i - 1] - previous; previous = a[i - 1]; }
for (std::size_t i = 1; i <= size; ++i) if (i + (i & -i) <= size) data[i + (i & -i)] += data[i];
}
void add(std::size_t i, T value) { for (++i; i < data.size(); i += i & -i) data[i] += value; }
T sum(std::size_t i) const { T result(0); for (; i; i -= i & -i) result += data[i]; return result; }
void write(std::vector<T>& a) {
for (std::size_t i = data.size() - 1; i; --i) if (i + (i & -i) < data.size()) data[i + (i & -i)] -= data[i];
T result(0); for (std::size_t i = 1; i < data.size(); ++i) { result += data[i]; a[i - 1] = result; }
}
};
T powerful(const std::vector<T>& base, std::size_t begin, U product, U limit) const {
T answer = base[product <= large ? big(product) : small(limit)];
for (std::size_t i = begin; i < first.size(); ++i) {
const U p = primes[i];
U power = p * p;
if (power > limit) break;
U next_limit = limit / power;
std::size_t correction = offsets[i];
for (;;) {
const T weight = corrections[correction++];
if constexpr (requires { weight == T(0); }) if (weight == T(0)) {
if (next_limit < p) break;
power *= p;
next_limit /= p;
continue;
}
if (i + 1 < first.size() && primes[i + 1] * primes[i + 1] <= next_limit)
answer += weight * powerful(base, i + 1, product * power, next_limit);
else answer += weight * base[product * power <= large ? big(product * power) : small(next_limit)];
if (next_limit < p) break;
power *= p;
next_limit /= p;
}
}
return answer;
}
template<bool SquareFree> void enumerate(Tree& tree, std::size_t begin, U product, T coefficient, U bound, unsigned depth = 0) const {
if (depth == 3) return;
for (std::size_t i = begin; i < first.size() && primes[i] <= bound / product; ++i) {
const U next = product * primes[i];
const T value = coefficient * first[i];
tree.add(next <= root ? small(next) : big(n / next), value);
enumerate<SquareFree>(tree, i + SquareFree, next, value, bound, depth + 1);
}
}
template<bool SquareFree> std::vector<T> build(const std::vector<T>* prefix, T inv3) const {
const T before = SquareFree ? (*prefix)[large] : T(0);
auto prime_large = [&](U d) {
if constexpr (SquareFree) return (*prefix)[d - 1] - before;
else return T(0);
};
const U tree_large = std::min<U>(std::floor(std::pow(n, 3.0 / 8)), large), tree_bound = n / (tree_large + 1);
const U min_cut = std::max<U>(tree_bound / (root + 1), fifth_root(tree_bound));
const U cut = std::max<U>(square_root(square_root(root)), min_cut);
const auto pi_cut = pi(cut), pi_fourth = pi(square_root(root));
std::vector<T> out(length, T(0));
out[0] = T(1);
for (std::size_t i = pi_cut; i < first.size(); ++i) out[small(primes[i])] = first[i];
for (U i = 1; i < root; ++i) out[i] += out[i - 1];
for (std::size_t i = pi_fourth, r = first.size(); i < first.size(); ++i) {
const U p = primes[i], limit = n / p;
while (r && U(r - 1) * primes[r - 1] > limit) --r;
for (std::size_t j = i + SquareFree; j < r; ++j) out[big(limit / primes[j])] += first[i] * first[j];
const U bound = std::min(primes[std::max(r, i + SquareFree)], root + 1);
if (p <= large) out[big(large / p)] += first[i] * out[small(root)];
if (bound <= limit) out[big(limit / bound)] -= first[i] * out[small(bound - 1)];
}
for (U i = large; i-- > 1;) out[big(i)] += out[big(i + 1)];
for (std::size_t i = pi_fourth, r = first.size(); i < first.size(); ++i) {
const U p = primes[i], limit = n / p;
while (r && U(r - 1) * primes[r - 1] > limit) --r;
const U bound = std::min(primes[std::max(r, i + SquareFree)], root + 1), split = large / p, end = limit / bound;
for (U j = end; j > split; --j) out[big(j)] += first[i] * out[small(limit / j)];
for (U j = split; j; --j) out[big(j)] += first[i] * prime_large(j * p);
}
const U bound_prime = primes[pi_fourth], bound_z = n / bound_prime / bound_prime / bound_prime;
for (U i = 1; i <= bound_z; ++i) {
const U limit = n / i;
const auto bound1 = pi(large / i), bound2 = pi(square_root(limit / bound_prime)), bound3 = pi(cube_root(limit));
T value(0);
for (std::size_t j = pi_fourth; j < bound1; ++j) value += first[j] * out[big(i * primes[j])];
for (std::size_t j = pi_fourth; j < bound2; ++j) {
const T correction = first[j] * first[j] * (out[small(limit / primes[j] / primes[j])] - out[small(bound_prime - 1)]);
if constexpr (SquareFree) value -= correction;
else value += correction;
}
for (std::size_t j = pi_fourth; j < bound3; ++j) value += first[j] * first[j] * first[j];
out[big(i)] += value * inv3;
}
for (U i = 1; i <= large; ++i) out[big(i)] += prime_large(i) + out[small(root)];
const std::size_t tree_length = length - tree_large;
Tree tree(out, tree_length);
for (std::size_t i = pi_fourth; i-- > pi_cut;) {
const U p = primes[i], limit = n / p, limit2 = limit / p;
const U split = std::min(large / p, tree_large), end = std::min(limit2, tree_large);
const U index2 = p * p <= root ? small(p * p) : std::min(big(limit2), tree_length);
auto access = [&](U index) { return index2 <= index && index < tree_length ? tree.sum(index + 1) : out[index]; };
if constexpr (SquareFree) {
const T previous = out[small(p)];
for (U j = 1; j <= split; ++j) out[big(j)] += first[i] * (access(big(j * p)) - previous);
for (U j = split + 1; j <= end; ++j) out[big(j)] += first[i] * (access(small(limit / j)) - previous);
enumerate<true>(tree, i + 1, p, first[i], tree_bound);
} else {
const T previous = out[small(p - 1)];
enumerate<false>(tree, i, p, first[i], tree_bound);
for (U j = end; j > split; --j) out[big(j)] += first[i] * (access(small(limit / j)) - previous);
for (U j = split; j; --j) out[big(j)] += first[i] * (access(big(j * p)) - previous);
}
}
tree.write(out);
for (std::size_t i = pi_cut; i--;) {
const U p = primes[i], limit = n / p, split = large / p;
if constexpr (SquareFree) {
for (U j = 1; j <= split; ++j) out[big(j)] += first[i] * out[big(j * p)];
for (U j = split + 1; j <= large; ++j) out[big(j)] += first[i] * out[small(limit / j)];
U j = root;
for (U q = root / p; q; --q) {
const T delta = first[i] * out[small(q)];
for (const U stop = q * p; j >= stop; --j) out[small(j)] += delta;
}
} else {
U j = p;
for (U q = 1; j <= root; ++q) {
const T delta = first[i] * out[small(q)];
for (const U stop = std::min(root + 1, (q + 1) * p); j < stop; ++j) out[small(j)] += delta;
}
for (U j = large; j > split; --j) out[big(j)] += first[i] * out[small(limit / j)];
for (U j = split; j; --j) out[big(j)] += first[i] * out[big(j * p)];
}
}
return out;
}
public:
Backend(const FloorQuotients& quotients, const std::vector<std::size_t>& prime_list) : n(quotients.max_n()), root(quotients.root()), large(quotients.large_size()), length(quotients.size()), primes(prime_list.begin(), prime_list.end()) { primes.push_back(root + 1); }
template<class F> T sum(const std::vector<T>& prefix, F& f, T inv3) {
if (!n) return T(0);
first.clear();
first.reserve(primes.size() - 1);
corrections.clear();
corrections.reserve(primes.size());
offsets.clear();
offsets.reserve(primes.size() - 1);
for (std::size_t i = 0; i + 1 < primes.size(); ++i) {
const U p = primes[i];
first.emplace_back(f(p, 1, p));
offsets.push_back(corrections.size());
T previous(0);
U power = p * p, limit = n / power;
for (unsigned e = 2;; ++e) {
previous = f(p, e, power) - first[i] * previous;
corrections.push_back(previous);
if (limit < p) break;
power *= p;
limit /= p;
}
}
auto out = build<true>(&prefix, inv3);
return powerful(out, 0, 1, n);
}
template<class Prefix> std::vector<T> prime_sum(Prefix& integer_prefix, T inv3) {
if (!n) return {};
first.clear();
for (std::size_t i = 0; i + 1 < primes.size(); ++i) first.emplace_back(integer_prefix(primes[i]) - integer_prefix(primes[i] - 1));
const auto base = build<false>(nullptr, inv3);
std::vector<T> result(length, T(0));
for (U i = 1; i <= large; ++i) result[i - 1] = integer_prefix(n / i) - base[big(i)];
for (std::size_t j = 0; j < first.size() && primes[j] <= large; ++j) {
const U p = primes[j], end = large / p;
for (U i = 1; i <= end; ++i) result[i - 1] -= first[j] * result[i * p - 1];
}
T cumulative(0);
std::size_t j = 0;
for (U i = 1; i <= root; ++i) {
if (i == primes[j]) cumulative += first[j++];
result[length - i] = cumulative;
}
for (U i = 0; i < large; ++i) result[i] += cumulative;
return result;
}
};
}
#line 1 "math/prime_sieve.hpp"
#line 5 "math/prime_sieve.hpp"
#include <array>
#include <bit>
#line 10 "math/prime_sieve.hpp"
#include <cstring>
#line 12 "math/prime_sieve.hpp"
inline std::vector<std::size_t> prime_sieve(std::size_t n);
template<class F> void for_each_prime(std::size_t n, F emit) {
if (n <= 2) return;
emit(std::size_t(2));
if (n <= 65537) {
const std::size_t size = n / 2;
std::vector<std::uint8_t> prime(size, 1);
prime[0] = 0;
for (std::size_t p = 3; p <= (n - 1) / p; p += 2) {
if (!prime[p / 2]) continue;
for (std::size_t j = p * p / 2; j < size; j += p) prime[j] = 0;
}
std::size_t i = 0;
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
for (; i + 8 <= size; i += 8) {
std::uint64_t word;
std::memcpy(&word, prime.data() + i, sizeof(word));
while (word) {
emit(2 * (i + (std::countr_zero(word) >> 3)) + 1);
word &= word - 1;
}
}
#endif
for (; i < size; ++i) if (prime[i]) emit(2 * i + 1);
return;
}
emit(3), emit(5);
constexpr std::array<unsigned, 8> residues{1, 7, 11, 13, 17, 19, 23, 29};
constexpr auto index = [&] {
std::array<unsigned, 30> index{};
for (unsigned i = 0; i < 8; ++i) index[residues[i]] = i;
return index;
}();
constexpr std::size_t period = 7 * 11 * 13, block_size = period * 32;
static const auto pattern = [&] {
std::array<std::uint8_t, period> pattern{};
for (std::size_t i = 0; i < period; ++i)
for (unsigned d = 0; d < 8; ++d) {
const auto x = 30 * i + residues[d];
pattern[i] |= std::uint8_t(bool(x % 7 && x % 11 && x % 13) << d);
}
return pattern;
}();
std::size_t root = std::sqrt(static_cast<long double>(n - 1));
while (root > (n - 1) / root) --root;
while (root + 1 <= (n - 1) / (root + 1)) ++root;
const auto primes = prime_sieve(root + 1);
struct State {
std::size_t p;
std::array<std::size_t, 8> next;
std::array<std::uint8_t, 8> mask;
};
std::vector<State> state;
state.reserve(primes.size());
for (const auto p : primes) {
if (p < 17) continue;
State current;
current.p = p;
for (unsigned d = 0; d < 8; ++d) {
const std::size_t k = p + (residues[d] + 30 - p % 30) % 30;
const __uint128_t x = __uint128_t(p) * k;
current.next[d] = x / 30;
current.mask[d] = std::uint8_t(~(1u << index[x % 30]));
}
state.push_back(current);
}
const std::size_t size = (n - 1) / 30 + 1;
std::vector<std::uint8_t> prime(block_size);
constexpr auto offsets = [&] {
std::array<unsigned, 64> offsets{};
for (unsigned i = 0; i < 64; ++i) offsets[i] = 30 * (i / 8) + residues[i % 8];
return offsets;
}();
for (std::size_t base = 0; base < size;) {
const std::size_t length = std::min(block_size, size - base);
for (std::size_t i = 0; i < length; i += period)
std::copy_n(pattern.data(), std::min(period, length - i), prime.data() + i);
if (base == 0) {
prime[0] &= ~1u;
prime[0] |= (1u << 1) | (1u << 2) | (1u << 3);
}
for (auto ¤t : state) {
const auto p = current.p;
for (unsigned d = 0; d < 8; ++d) {
std::size_t j = current.next[d];
const auto mask = current.mask[d];
for (; j + 3 * p < length; j += 4 * p) {
prime[j] &= mask;
prime[j + p] &= mask;
prime[j + 2 * p] &= mask;
prime[j + 3 * p] &= mask;
}
for (; j < length; j += p) prime[j] &= mask;
current.next[d] = j - length;
}
}
if (base + length == size) {
const auto remaining = n - 30 * (size - 1);
for (unsigned d = 0; d < 8; ++d) if (residues[d] >= remaining) prime[length - 1] &= ~(1u << d);
}
std::fill(prime.begin() + length, prime.begin() + ((length + 7) / 8 * 8), 0);
for (std::size_t i = 0; i < length; i += 8) {
std::uint64_t word = 0;
std::memcpy(&word, prime.data() + i, sizeof(word));
if constexpr (std::endian::native == std::endian::big) word = std::byteswap(word);
else if constexpr (std::endian::native != std::endian::little) {
word = 0;
for (unsigned d = 0; d < 8; ++d) word |= std::uint64_t(prime[i + d]) << (8 * d);
}
while (word) {
const auto x = 30 * (base + i) + offsets[std::countr_zero(word)];
emit(x);
word &= word - 1;
}
}
base += length;
}
}
inline std::vector<std::size_t> prime_sieve(std::size_t n) {
std::vector<std::size_t> result;
if (n >= 1024) result.reserve(std::size_t(double(n) / (std::log(double(n)) - 1.1)));
for_each_prime(n, [&](std::size_t p) { result.push_back(p); });
return result;
}
#line 13 "math/multiplicative_prefix_sum.hpp"
template<class T> class MultiplicativePrefixSum {
FloorQuotients quotients_;
std::vector<std::size_t> primes_;
bool use_backend(T& inverse) const {
if constexpr (requires { std::uint64_t(T::mod()); T(0) == T(0); }) {
const auto modulus = std::uint64_t(T::mod());
if (max_n() >= (UINT64_C(1) << 32) && modulus > 1 && modulus <= UINT32_MAX && modulus % 3) {
inverse = T((modulus * (modulus % 3 == 1 ? 2 : 1) + 1) / 3);
return T(3) * inverse == T(1);
}
}
return false;
}
template<class R, class Prefix> std::vector<R> prime_sum_impl(Prefix&& integer_prefix) const {
const std::uint64_t n = max_n(), root = quotients_.root();
const std::size_t large = quotients_.large_size(), length = size(), half = (root + 1) / 2;
const R two = n >= 2 ? integer_prefix(2) - integer_prefix(1) : R(0);
std::vector<R> result, small;
result.reserve(length), small.reserve(half);
auto initial = [&](std::uint64_t x) { return x == 1 ? R(0) : integer_prefix(x) - two * integer_prefix(x / 2) - R(1); };
for (std::size_t i = 0; i < large; ++i) result.push_back(initial(quotients()[i]));
for (std::size_t i = 0; i < half; ++i) small.push_back(initial(2 * i + 1));
for (std::uint64_t p : primes_) {
if (p == 2) continue;
const R weight = integer_prefix(p) - integer_prefix(p - 1), before = small[(p - 2) / 2];
const auto end = std::min<std::uint64_t>(large, n / (p * p));
const auto split = std::min<std::uint64_t>(end, large / p);
std::uint64_t i = 1;
for (; i <= split; ++i) result[i - 1] -= weight * (result[i * p - 1] - before);
for (; i <= end; ++i) result[i - 1] -= weight * (small[(n / (i * p) - 1) / 2] - before);
std::size_t x = half;
for (std::uint64_t q = (root / p - 1) | 1; q >= p; q -= 2) {
const R delta = weight * (small[(q - 1) / 2] - before);
for (const auto stop = (q * p + 1) / 2; x >= stop; --x) small[x - 1] -= delta;
}
}
for (auto& value : result) value += two;
for (auto x = root; x; --x) result.push_back(small[(x - 1) / 2] + (x >= 2 ? two : R(0)));
return result;
}
public:
explicit MultiplicativePrefixSum(std::uint64_t n = 0) : quotients_(n), primes_(prime_sieve(quotients_.root() + 1)) {}
MultiplicativePrefixSum(const MultiplicativePrefixSum&) = default;
MultiplicativePrefixSum& operator=(const MultiplicativePrefixSum&) = default;
MultiplicativePrefixSum(MultiplicativePrefixSum&& other) noexcept : MultiplicativePrefixSum() { swap(other); }
MultiplicativePrefixSum& operator=(MultiplicativePrefixSum&& other) noexcept {
if (this != &other) { MultiplicativePrefixSum empty; swap(empty); swap(other); }
return *this;
}
void swap(MultiplicativePrefixSum& other) noexcept { quotients_.swap(other.quotients_); primes_.swap(other.primes_); }
std::uint64_t max_n() const { return quotients_.max_n(); }
std::size_t size() const { return quotients_.size(); }
const std::vector<std::uint64_t>& quotients() const { return quotients_.values(); }
std::size_t index(std::uint64_t x) const { return quotients_.index(x); }
T get(const std::vector<T>& table, std::uint64_t x) const {
assert(table.size() == size());
return x ? table[index(x)] : T(0);
}
template<class Prefix> std::vector<T> prime_sum(Prefix integer_prefix) const {
T inverse(0);
if (use_backend(inverse)) return multiplicative_sum_detail::Backend<T>(quotients_, primes_).prime_sum(integer_prefix, inverse);
return prime_sum_impl<T>(integer_prefix);
}
std::vector<T> prime_sum_linear(T constant, T linear) const {
auto identity_prefix = [](std::uint64_t x) -> T {
return x & 1 ? T(x) * (T(x / 2) + T(1)) : T(x / 2) * (T(x) + T(1));
};
T inverse(0);
const bool split = use_backend(inverse);
if constexpr (requires { linear == T(0); }) {
if (linear == T(0)) {
if (constant == T(0)) return std::vector<T>(size(), T(0));
if (split) {
auto prefix = [](std::uint64_t x) { return T(x); };
auto result = multiplicative_sum_detail::Backend<T>(quotients_, primes_).prime_sum(prefix, inverse);
for (auto& value : result) value *= constant;
return result;
}
auto counts = prime_sum_impl<std::uint64_t>([](std::uint64_t x) { return x; });
std::vector<T> result;
result.reserve(size());
for (auto value : counts) result.push_back(T(value) * constant);
return result;
}
if (constant == T(0)) {
auto result = prime_sum(identity_prefix);
for (auto& value : result) value *= linear;
return result;
}
}
if (split) {
using Weights = multiplicative_sum_detail::LinearWeights<T>;
auto prefix = [&](std::uint64_t x) { return Weights(T(x), identity_prefix(x)); };
auto sums = multiplicative_sum_detail::Backend<Weights>(quotients_, primes_).prime_sum(prefix, Weights(inverse));
std::vector<T> result;
result.reserve(size());
for (const auto& value : sums) result.push_back(value.count * constant + value.weighted * linear);
return result;
}
const std::uint64_t n = max_n(), root = quotients_.root();
const std::size_t large = quotients_.large_size(), length = size(), half = (root + 1) / 2;
std::vector<T> result, small;
std::vector<std::uint64_t> count;
std::vector<std::uint32_t> count_small;
result.reserve(length), small.reserve(half);
count.reserve(large), count_small.reserve(half);
for (std::size_t i = 0; i < large; ++i) {
const auto x = quotients()[i], c = x / 2 + (x & 1);
count.push_back(std::uint64_t(c - 1));
result.push_back(T(c) * T(c) - T(1));
}
for (std::size_t i = 0; i < half; ++i) {
count_small.push_back(std::uint32_t(i));
small.push_back(T(i + 1) * T(i + 1) - T(1));
}
for (std::uint64_t p : primes_) {
if (p == 2) continue;
const T weight = T(p), before = small[(p - 2) / 2];
const std::uint64_t count_before = count_small[(p - 2) / 2];
const auto end = std::min<std::uint64_t>(large, n / (p * p));
const auto split = std::min<std::uint64_t>(end, large / p);
std::uint64_t i = 1;
for (; i <= split; ++i) {
count[i - 1] -= count[i * p - 1] - count_before;
result[i - 1] -= weight * (result[i * p - 1] - before);
}
for (; i <= end; ++i) {
const auto at = (n / (i * p) - 1) / 2;
count[i - 1] -= count_small[at] - count_before;
result[i - 1] -= weight * (small[at] - before);
}
std::size_t x = half;
for (std::uint64_t q = (root / p - 1) | 1; q >= p; q -= 2) {
const std::uint64_t count_delta = count_small[(q - 1) / 2] - count_before;
const T delta = weight * (small[(q - 1) / 2] - before);
for (const auto stop = (q * p + 1) / 2; x >= stop; --x) {
count_small[x - 1] -= count_delta;
small[x - 1] -= delta;
}
}
}
for (std::size_t i = 0; i < large; ++i) result[i] = (T(count[i]) + T(1)) * constant + (result[i] + T(2)) * linear;
for (auto x = root; x; --x) result.push_back((T(count_small[(x - 1) / 2]) + T(x >= 2)) * constant + (small[(x - 1) / 2] + T(x >= 2 ? 2 : 0)) * linear);
return result;
}
template<class PrimePower> std::vector<T> prefix_sum(const std::vector<T>& prime_prefix, PrimePower prime_power) const {
assert(prime_prefix.size() == size());
std::vector<T> result = prime_prefix, weights;
std::vector<std::uint64_t> powers;
weights.reserve(64), powers.reserve(64);
for (std::size_t j = primes_.size(); j--;) {
const std::uint64_t p = primes_[j];
const T before = prime_prefix[index(p)];
weights.clear(), powers.clear();
for (std::uint64_t power = p;; power *= p) {
powers.push_back(power);
weights.push_back(prime_power(p, static_cast<unsigned>(powers.size()), power));
if (power > max_n() / p) break;
}
const T first = weights[0], second = weights[1];
const auto length = size(), large = quotients_.large_size();
const auto end = std::min<std::uint64_t>(large, max_n() / (p * p));
std::size_t i = 0;
if (powers.size() > 2) for (; i < end && quotients()[i] >= powers[2]; ++i) {
const auto d0 = (i + 1) * p;
T delta = first * (result[d0 <= large ? d0 - 1 : length - max_n() / d0] - before) + second;
for (std::size_t e = 1; e + 1 < powers.size() && powers[e + 1] <= quotients()[i]; ++e) {
const auto d = (i + 1) * powers[e];
delta += weights[e] * (result[d <= large ? d - 1 : length - max_n() / d] - before) + weights[e + 1];
}
result[i] += delta;
}
const auto split = std::min<std::uint64_t>(end, large / p);
for (; i < split; ++i)
result[i] += first * (result[(i + 1) * p - 1] - before) + second;
for (; i < end; ++i)
result[i] += first * (result[length - max_n() / ((i + 1) * p)] - before) + second;
std::uint64_t x = quotients_.root();
while (x >= p * p) {
const auto q = x / p, stop = q * p;
T delta = first * (result[length - q] - before) + second;
for (std::size_t e = 1; e + 1 < powers.size() && powers[e + 1] <= x; ++e)
delta += weights[e] * (result[length - x / powers[e]] - before) + weights[e + 1];
for (; x >= stop; --x) result[length - x] += delta;
}
}
for (auto& value : result) value += T(1);
return result;
}
template<class PrimePower> T sum(const std::vector<T>& prime_prefix, PrimePower prime_power) const {
assert(prime_prefix.size() == size());
if (!max_n()) return T(0);
T inverse(0);
if (use_backend(inverse)) return multiplicative_sum_detail::Backend<T>(quotients_, primes_).sum(prime_prefix, prime_power, inverse);
std::vector<T> first, second, square_prefix{T(0)};
first.reserve(primes_.size()), second.reserve(primes_.size()), square_prefix.reserve(primes_.size() + 1);
for (std::uint64_t p : primes_) {
first.push_back(prime_power(p, 1, p));
second.push_back(prime_power(p, 2, p * p));
square_prefix.push_back(square_prefix.back() + second.back());
}
T result = prime_prefix[0] + T(1);
const auto length = size(), large = quotients_.large_size();
auto dfs = [&](auto&& self, std::size_t i, unsigned e, std::uint64_t product, std::uint64_t power) -> T {
const std::uint64_t p = primes_[i], limit = max_n() / product;
T delta = e == 1 ? second[i] : prime_power(p, e + 1, power * p);
if (p * p <= limit) delta += self(self, i, e + 1, product * p, power * p);
T weight = e == 1 ? first[i] : e == 2 ? second[i] : prime_power(p, e, power);
if constexpr (requires { weight == T(0); }) if (weight == T(0)) return delta;
T tail = prime_prefix[product <= large ? product - 1 : length - limit] - prime_prefix[length - p];
std::size_t j = i + 1;
for (; j < primes_.size(); ++j) {
const std::uint64_t q = primes_[j];
if (__uint128_t(q) * q * q > limit) break;
tail += self(self, j, 1, product * q, q);
}
const auto begin = j;
for (; j < primes_.size(); ++j) {
const std::uint64_t q = primes_[j];
if (q * q > limit) break;
tail += first[j] * (prime_prefix[product * q <= large ? product * q - 1 : length - limit / q] - prime_prefix[length - q]);
}
return delta + weight * (tail + square_prefix[j] - square_prefix[begin]);
};
for (std::size_t i = 0; i < primes_.size(); ++i) result += dfs(dfs, i, 1, primes_[i], primes_[i]);
return result;
}
};