imsuck's library

This documentation is automatically generated by competitive-verifier/competitive-verifier

View the Project on GitHub imsuck/library

:heavy_check_mark: Zeta-Moebius Transform (math/zeta_moebius_transform.hpp)

Description

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]

Operations

Note

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

References

Verified with

Code

#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;
}
Back to top page