This documentation is automatically generated by competitive-verifier/competitive-verifier
#include "math/zeta_moebius_transform.hpp"Zeta-Moebius Transform provides fast convolution operations on sequences indexed by integers. The divisor zeta transform, given a function $f$, computes a function $g$ satisfying:
| [g(n) = \sum_{m | n} f(m)] |
The Möbius transform is the inverse of the zeta transform (given $g$, it computes $f$). The multiple transform is defined similarly.
Using divisor FZT/FMT, LCM convolution can be computed efficiently:
[c_k = \sum_{\mathrm{lcm}(i,j)=k} a_i b_j]
Using multiple FZT/FMT, GCD convolution can be computed efficiently:
[c_k = \sum_{\gcd(i,j)=k} a_i b_j]
multiple_fzt<T>(vector<T> &a)
a in-place: a[k] = sum_{k|d} a[d]
multiple_fmt<T>(vector<T> &a)
multiple_fzt)a in-placedivisor_fzt<T>(vector<T> &a)
a in-place: a[k] = sum_{d|k} a[d]
divisor_fmt<T>(vector<T> &a)
divisor_fzt)a in-placegcd_convolution<T>(vector<T> a, vector<T> b)
c[k] = sum_{gcd(i,j)=k} a[i] * b[j]
lcm_convolution<T>(vector<T> a, vector<T> b)
c[k] = sum_{lcm(i,j)=k} a[i] * b[j]
The following table shows which transforms are used for different convolution operations:
| Convolution | Transform | Inverse Transform |
|---|---|---|
| $\max$ | zeta transform (prefix sum, lower) | Möbius transform (prefix sum, lower) |
| $\min$ | zeta transform (prefix sum, upper) | Möbius transform (prefix sum, upper) |
| $\gcd$ | zeta transform (multiple) | Möbius transform (multiple) |
| $\mathrm{lcm}$ | zeta transform (divisor) | Möbius transform (divisor) |
| $\mathrm{bitwise\ and}$ | zeta transform (bit, upper) | Möbius transform (bit, upper) |
| $\mathrm{bitwise\ or}$ | zeta transform (bit, lower) | Möbius transform (bit, lower) |
| $\mathrm{bitwise\ xor}$ | Walsh-Hadamard transform | inverse Walsh-Hadamard transform |
| $+$ | Fourier transform, NTT | inverse Fourier transform, inverse NTT |
#pragma once
template<class T> void multiple_fzt(vector<T> &a) {
const int n = (int)a.size();
vector<char> sieve(n, true);
for (int p = 2; p < n; p += 1 + p % 2) {
if (!sieve[p]) continue;
for (int k = (n - 1) / p; k > 0; k--) {
sieve[k * p] = false;
a[k] += a[k * p];
}
}
}
template<class T> void multiple_fmt(vector<T> &a) {
const int n = (int)a.size();
vector<char> sieve(n, true);
for (int p = 2; p < n; p += 1 + p % 2) {
if (!sieve[p]) continue;
for (int k = 1; k * p < n; k++) {
sieve[k * p] = false;
a[k] -= a[k * p];
}
}
}
template<class T> void divisor_fzt(vector<T> &a) {
const int n = (int)a.size();
vector<char> sieve(n, true);
for (int p = 2; p < n; p += 1 + p % 2) {
if (!sieve[p]) continue;
for (int k = 1; k * p < n; k++) {
sieve[k * p] = false;
a[k * p] += a[k];
}
}
}
template<class T> void divisor_fmt(vector<T> &a) {
const int n = (int)a.size();
vector<char> sieve(n, true);
for (int p = 2; p < n; p += 1 + p % 2) {
if (!sieve[p]) continue;
for (int k = (n - 1) / p; k > 0; k--) {
sieve[k * p] = false;
a[k * p] -= a[k];
}
}
}
template<class T> vector<T> gcd_convolution(vector<T> a, vector<T> b) {
assert(a.size() == b.size());
multiple_fzt(a), multiple_fzt(b);
for (int i = 0; i < a.size(); i++) a[i] *= b[i];
multiple_fmt(a);
return a;
}
template<class T> vector<T> lcm_convolution(vector<T> a, vector<T> b) {
assert(a.size() == b.size());
divisor_fzt(a), divisor_fzt(b);
for (int i = 0; i < a.size(); i++) a[i] *= b[i];
divisor_fmt(a);
return a;
}
#line 2 "math/zeta_moebius_transform.hpp"
template<class T> void multiple_fzt(vector<T> &a) {
const int n = (int)a.size();
vector<char> sieve(n, true);
for (int p = 2; p < n; p += 1 + p % 2) {
if (!sieve[p]) continue;
for (int k = (n - 1) / p; k > 0; k--) {
sieve[k * p] = false;
a[k] += a[k * p];
}
}
}
template<class T> void multiple_fmt(vector<T> &a) {
const int n = (int)a.size();
vector<char> sieve(n, true);
for (int p = 2; p < n; p += 1 + p % 2) {
if (!sieve[p]) continue;
for (int k = 1; k * p < n; k++) {
sieve[k * p] = false;
a[k] -= a[k * p];
}
}
}
template<class T> void divisor_fzt(vector<T> &a) {
const int n = (int)a.size();
vector<char> sieve(n, true);
for (int p = 2; p < n; p += 1 + p % 2) {
if (!sieve[p]) continue;
for (int k = 1; k * p < n; k++) {
sieve[k * p] = false;
a[k * p] += a[k];
}
}
}
template<class T> void divisor_fmt(vector<T> &a) {
const int n = (int)a.size();
vector<char> sieve(n, true);
for (int p = 2; p < n; p += 1 + p % 2) {
if (!sieve[p]) continue;
for (int k = (n - 1) / p; k > 0; k--) {
sieve[k * p] = false;
a[k * p] -= a[k];
}
}
}
template<class T> vector<T> gcd_convolution(vector<T> a, vector<T> b) {
assert(a.size() == b.size());
multiple_fzt(a), multiple_fzt(b);
for (int i = 0; i < a.size(); i++) a[i] *= b[i];
multiple_fmt(a);
return a;
}
template<class T> vector<T> lcm_convolution(vector<T> a, vector<T> b) {
assert(a.size() == b.size());
divisor_fzt(a), divisor_fzt(b);
for (int i = 0; i < a.size(); i++) a[i] *= b[i];
divisor_fmt(a);
return a;
}