cp-library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub rniya/cp-library

:heavy_check_mark: GCD Convolution
(src/convolution/gcd_convolution.hpp)

入力

ともに長さ $n$ の数列 $a _ 1, \dots , a _ n$ 及び $b _ 1, \dots , b _ n$.

出力

数列 $a$ と $b$ の index の最大公約数についての畳み込み. すなわち,

\[c _ k = \sum _ {\gcd(i, j) = k} a _ i b _ j\]

で定義される長さ $n$ の数列 $c$.

計算量

時間計算量 $\mathrm{O}(n \log \log n)$

出題例

Verified with

Code

#pragma once
#include <cassert>
#include <vector>

namespace internal {

// f(k) <- \sum_{k | i} f(i)
template <typename T> void divisor_transform(std::vector<T>& f) {
    int n = f.size();
    std::vector<bool> sieve(n, true);
    for (int p = 2; p < n; p++) {
        if (sieve[p]) {
            for (int k = (n - 1) / p; k > 0; k--) {
                sieve[k * p] = false;
                f[k] += f[k * p];
            }
        }
    }
    for (int i = 1; i < n; i++) f[i] += f[0];
}

// inverse of divisor transform
template <typename T> void inverse_divisor_transform(std::vector<T>& f) {
    int n = f.size();
    std::vector<bool> sieve(n, true);
    for (int i = 1; i < n; i++) f[i] -= f[0];
    for (int p = 2; p < n; p++) {
        if (sieve[p]) {
            for (int k = 1 / p; k * p < n; k++) {
                sieve[k * p] = false;
                f[k] -= f[k * p];
            }
        }
    }
}

}  // namespace internal

template <typename T> std::vector<T> gcd_convolution(std::vector<T> f, std::vector<T> g) {
    assert(f.size() == g.size());
    internal::divisor_transform(f);
    internal::divisor_transform(g);
    for (int i = 0; i < int(f.size()); i++) f[i] *= g[i];
    internal::inverse_divisor_transform(f);
    return f;
}
#line 2 "src/convolution/gcd_convolution.hpp"
#include <cassert>
#include <vector>

namespace internal {

// f(k) <- \sum_{k | i} f(i)
template <typename T> void divisor_transform(std::vector<T>& f) {
    int n = f.size();
    std::vector<bool> sieve(n, true);
    for (int p = 2; p < n; p++) {
        if (sieve[p]) {
            for (int k = (n - 1) / p; k > 0; k--) {
                sieve[k * p] = false;
                f[k] += f[k * p];
            }
        }
    }
    for (int i = 1; i < n; i++) f[i] += f[0];
}

// inverse of divisor transform
template <typename T> void inverse_divisor_transform(std::vector<T>& f) {
    int n = f.size();
    std::vector<bool> sieve(n, true);
    for (int i = 1; i < n; i++) f[i] -= f[0];
    for (int p = 2; p < n; p++) {
        if (sieve[p]) {
            for (int k = 1 / p; k * p < n; k++) {
                sieve[k * p] = false;
                f[k] -= f[k * p];
            }
        }
    }
}

}  // namespace internal

template <typename T> std::vector<T> gcd_convolution(std::vector<T> f, std::vector<T> g) {
    assert(f.size() == g.size());
    internal::divisor_transform(f);
    internal::divisor_transform(g);
    for (int i = 0; i < int(f.size()); i++) f[i] *= g[i];
    internal::inverse_divisor_transform(f);
    return f;
}
Back to top page