This documentation is automatically generated by online-judge-tools/verification-helper
#include "convolution/gcd_convolution.hpp"
長さ$N+1$ の整数列 $a$, $b$ について、 $c_k = \sum_{gcd(i, j) = k} a_i b_j$ を $O(N\log \log N)$ で計算する。
#pragma once
#include "../math/multiple_transform.hpp"
namespace ebi {
template <class mint>
std::vector<mint> gcd_convolution(const std::vector<mint> &a,
const std::vector<mint> &b) {
int n = a.size();
assert(a.size() == b.size());
auto ra = multiple_transform::zeta_transform(a);
auto rb = multiple_transform::zeta_transform(b);
for (int i = 0; i < n; i++) {
ra[i] *= rb[i];
}
return multiple_transform::mobius_transform(ra);
}
} // namespace ebi
#line 2 "convolution/gcd_convolution.hpp"
#line 2 "math/multiple_transform.hpp"
#include <vector>
#line 2 "math/eratosthenes_sieve.hpp"
#include <cassert>
#include <cstdint>
#line 6 "math/eratosthenes_sieve.hpp"
/*
reference: https://37zigen.com/sieve-eratosthenes/
*/
namespace ebi {
struct eratosthenes_sieve {
private:
using i64 = std::int_fast64_t;
int n;
std::vector<bool> table;
public:
eratosthenes_sieve(int _n) : n(_n), table(std::vector<bool>(n + 1, true)) {
table[1] = false;
for (i64 i = 2; i * i <= n; i++) {
if (!table[i]) continue;
for (i64 j = i; i * j <= n; j++) {
table[i * j] = false;
}
}
}
bool is_prime(int p) {
return table[p];
}
std::vector<int> prime_table(int m = -1) {
if (m < 0) m = n;
std::vector<int> prime;
for (int i = 2; i <= m; i++) {
if (table[i]) prime.emplace_back(i);
}
return prime;
}
};
} // namespace ebi
#line 6 "math/multiple_transform.hpp"
namespace ebi {
struct multiple_transform {
multiple_transform() = default;
template <class mint>
static std::vector<mint> zeta_transform(const std::vector<mint> &f) {
int n = f.size() - 1;
auto F = f;
if (m < n) {
while (m < n) m <<= 1;
eratosthenes_sieve sieve(m);
primes = sieve.prime_table();
}
for (const auto &p : primes) {
if (n < p) break;
for (int i = n / p; i > 0; i--) F[i] += F[i * p];
}
return F;
}
template <class mint>
static std::vector<mint> mobius_transform(const std::vector<mint> &F) {
int n = F.size() - 1;
auto f = F;
if (m < n) {
while (m < n) m <<= 1;
eratosthenes_sieve sieve(m);
primes = sieve.prime_table();
}
for (const auto &p : primes) {
if (n < p) break;
for (int i = 1; i * p <= n; i++) f[i] -= f[i * p];
}
return f;
}
private:
static int m;
static std::vector<int> primes;
};
int multiple_transform::m = 4;
std::vector<int> multiple_transform::primes = {2, 3};
} // namespace ebi
#line 4 "convolution/gcd_convolution.hpp"
namespace ebi {
template <class mint>
std::vector<mint> gcd_convolution(const std::vector<mint> &a,
const std::vector<mint> &b) {
int n = a.size();
assert(a.size() == b.size());
auto ra = multiple_transform::zeta_transform(a);
auto rb = multiple_transform::zeta_transform(b);
for (int i = 0; i < n; i++) {
ra[i] *= rb[i];
}
return multiple_transform::mobius_transform(ra);
}
} // namespace ebi