cp-library

競技プログラミング用 C++ ライブラリ

View the Project on GitHub qandori/cp-library

:heavy_check_mark: Divisor Zeta / Möbius Transform

components/divisor_zeta.hpp · GitHub で見る

共通ページ

#include "lib/components/divisor_zeta.hpp"

std::vector<long long> a{0, 1, 2, 3, 4};
divisor_zeta(a); // {0,1,3,4,7}
divisor_zeta<true>(a); // {0,1,2,3,4}
操作 内容・返り値 計算量
divisor_zeta<inverse = false,multiples = false>(a) 約数和で上書き。multiples = true は倍数和、inverse = true は逆変換 \(O(n\log\log n)\)
divisor_zeta<inverse,multiples>(a,primes) 事前計算した prime_sieve(a.size()) を再利用 \(O(n\log\log n)\)

divisor_zeta<inverse = false, multiples = false>(a) は約数和、multiples = true なら倍数和を計算します。inverse = true は逆変換です。添字 0 は使いません。

同じ長さで繰り返す場合は prime_sieve(a.size()) の結果を第 2 引数に渡せます。

既定では、変換後の a[i] が元の配列の a[d](d は i の約数)の和です。例の添字 4 は 1+2+4=7 になります。multiples=true なら i の倍数の添字を合計します。配列をその場で書き換え、戻り値は void です。逆変換でも同じ multiples を指定します。

長さを \(n\) として時間は \(O(n\log\log n)\)、追加空間は素数列を渡す場合 \(O(1)\)、省略する場合 \(O(n)\) です。要素の演算は \(O(1)\) とします。

依存ライブラリ

検証コード

コード

#ifndef DIVISOR_ZETA_HPP
#define DIVISOR_ZETA_HPP

#include "../math/prime_sieve.hpp"
#include <cstddef>
#include <vector>

namespace divisor_zeta_detail {

template <bool inverse, bool multiples, class F>
void for_each(std::size_t n, const std::vector<std::size_t> &ps, std::size_t count, F update) {
	if (!n) return;
	for (std::size_t j = 0; j < count; ++j) {
		const std::size_t p = ps[j], end = (n - 1) / p;
		auto apply = [&](std::size_t i) { update(multiples ? i : i * p, multiples ? i * p : i); };
		if constexpr (inverse == multiples) {
			std::size_t i = 1;
			for (; i + 3 <= end; i += 4) apply(i), apply(i + 1), apply(i + 2), apply(i + 3);
			for (; i <= end; ++i) apply(i);
		} else {
			std::size_t i = end;
			for (; i >= 4; i -= 4) apply(i), apply(i - 1), apply(i - 2), apply(i - 3);
			for (; i; --i) apply(i);
		}
	}
}

}

template <bool inverse = false, bool multiples = false, class T>
void divisor_zeta(std::vector<T> &a, const std::vector<std::size_t> &ps) {
	divisor_zeta_detail::for_each<inverse, multiples>(a.size(), ps, ps.size(), [&](std::size_t to, std::size_t from) {
		if constexpr (inverse) a[to] -= a[from];
		else a[to] += a[from];
	});
}

template <bool inverse = false, bool multiples = false, class T>
void divisor_zeta(std::vector<T> &a) { divisor_zeta<inverse, multiples>(a, prime_sieve(a.size())); }

#endif
Back to top page