This documentation is automatically generated by online-judge-tools/verification-helper
View the Project on GitHub tkmst201/Library
#define PROBLEM "https://judge.yosupo.jp/problem/convolution_mod_1000000007" #include "Convolution/NumberTheoreticTransform_AnyMod.hpp" #include <cstdio> #include <vector> int main() { int N, M; scanf("%d %d", &N, &M); std::vector<int> A(N), B(M); for (int i = 0; i < N; ++i) scanf("%d", &A[i]); for (int i = 0; i < M; ++i) scanf("%d", &B[i]); auto ans = NumberTheoreticTransform_AnyMod<1'000'000'007>::multiply(A, B); for (int i = 0; i < N + M - 1; ++i) printf("%d%c", ans[i], i == N + M - 1 ? '\n': ' '); }
#line 1 "Test/NumberTheoreticTransform_AnyMod.test.cpp" #define PROBLEM "https://judge.yosupo.jp/problem/convolution_mod_1000000007" #line 1 "Convolution/NumberTheoreticTransform_AnyMod.hpp" #line 1 "Convolution/NumberTheoreticTransform.hpp" #line 1 "Mathematics/mod_pow_inv.hpp" #include <cassert> #include <type_traits> /** * @brief https://tkmst201.github.io/Library/Mathematics/mod_pow_inv.hpp */ namespace tk { template<typename T> constexpr T mod_pow(T x, T n, T m) noexcept { static_assert(std::is_integral<T>::value); assert(m > 0); assert(n >= 0); x = x % m + (x >= 0 ? 0 : m); T res = 1 % m; while (n > 0) { if (n & 1) res = res * x % m; x = x * x % m; n >>= 1; } return res; } template<typename T> constexpr T mod_inv(T x, T m) noexcept { static_assert(std::is_integral<T>::value); static_assert(std::is_signed<T>::value); assert(m > 0); x = x % m + (x >= 0 ? 0 : m); T x1 = 1, y = m, y1 = 0; while (y > 0) { const T q = x / y; T tmp = x - q * y; x = y; y = tmp; tmp = x1 - q * y1; x1 = y1; y1 = tmp; } assert(x == 1); if (x1 == m) x1 = 0; if (x1 < 0) x1 += m; return x1; } } // namespace tk #line 5 "Convolution/NumberTheoreticTransform.hpp" #include <vector> #include <utility> #line 9 "Convolution/NumberTheoreticTransform.hpp" #include <cstdint> /** * @brief https://tkmst201.github.io/Library/Convolution/NumberTheoreticTransform.hpp */ template<int MOD, int PRIMITIVE_ROOT> struct NumberTheoreticTransform { static_assert(MOD > 0); static_assert(PRIMITIVE_ROOT > 0); private: using uint32 = std::uint32_t; using calc_type = long long; public: template<typename T> static std::vector<T> multiply(const std::vector<T> & a, const std::vector<T> & b) { static_assert(std::is_integral<T>::value); if (a.empty() || b.empty()) return {}; const uint32 n_ = a.size() + b.size() - 1; uint32 n = 1; while (n < n_) n <<= 1; { uint32 two_exp = 0; int tm = MOD - 1; while (tm > 0 && (~tm & 1)) ++two_exp, tm >>= 1; assert((1u << two_exp) >= n); } std::vector<T> c(n, 0); for (uint32 i = 0; i < a.size(); ++i) c[i] = a[i] % MOD + (a[i] >= 0 ? 0 : MOD); ntt(c); std::vector<T> d(n, 0); for (uint32 i = 0; i < b.size(); ++i) d[i] = b[i] % MOD + (b[i] >= 0 ? 0 : MOD); ntt(d); const int ninv = tk::mod_inv<int>(n, MOD); for (uint32 i = 0; i < n; ++i) c[i] = static_cast<calc_type>(c[i]) * d[i] % MOD * ninv % MOD; d.clear(); ntt(c, true); c.resize(a.size() + b.size() - 1); return c; } private: template<typename T> static void ntt(std::vector<T> & a, bool inv = false) { const uint32 n = a.size(); int nroot = tk::mod_pow<calc_type>(PRIMITIVE_ROOT, (MOD - 1) / n, MOD); if (inv) nroot = tk::mod_inv(nroot, MOD); for (uint32 w = n; w > 1; w >>= 1) { const uint32 m = w >> 1; std::vector<int> omega(m, 0); omega[0] = 1; for (uint32 i = 1; i < m; ++i) omega[i] = static_cast<calc_type>(omega[i - 1]) * nroot % MOD; const int half = static_cast<calc_type>(omega.back()) * nroot % MOD; for (uint32 p = 0; p < n; p += w) { for (uint32 i = p; i < p + m; ++i) { const calc_type x = a[i], y = a[i + m]; a[i] = (x + y) % MOD; a[i + m] = (x + y * half % MOD) % MOD * omega[i - p] % MOD; } } nroot = static_cast<calc_type>(nroot) * nroot % MOD; } bit_reverse(a); } template<typename T> static void bit_reverse(std::vector<T> & a) noexcept { const uint32 n = a.size(); for (uint32 i = 1, j = 0; i < n - 1; ++i) { for (uint32 k = n >> 1; k > (j ^= k); k >>= 1); if (i < j) std::swap(a[i], a[j]); } } }; #line 1 "Mathematics/garner.hpp" #line 7 "Mathematics/garner.hpp" #line 1 "Mathematics/euclid.hpp" #line 6 "Mathematics/euclid.hpp" #include <tuple> #line 8 "Mathematics/euclid.hpp" #include <cmath> /** * @brief https://tkmst201.github.io/Library/Mathematics/euclid.hpp */ namespace tk { template<typename T> constexpr T gcd(T a, T b) noexcept { static_assert(std::is_integral<T>::value); assert(a >= 0); assert(b >= 0); while (b != 0) { const T t = a % b; a = b; b = t; } return a; } template<typename T> constexpr T lcm(T a, T b) noexcept { static_assert(std::is_integral<T>::value); assert(a >= 0); assert(b >= 0); if (a == 0 || b == 0) return 0; return a / gcd(a, b) * b; } template<typename T> constexpr std::tuple<T, T, T> ext_gcd(T a, T b) noexcept { static_assert(std::is_integral<T>::value); static_assert(std::is_signed<T>::value); assert(a != 0); assert(b != 0); T a1 = (a > 0) * 2 - 1, a2 = 0, b1 = 0, b2 = (b > 0) * 2 - 1; a = std::abs(a); b = std::abs(b); while (b > 0) { const T q = a / b; T tmp = a - q * b; a = b; b = tmp; tmp = a1 - q * b1; a1 = b1; b1 = tmp; tmp = a2 - q * b2; a2 = b2; b2 = tmp; } return {a, a1, a2}; } } // namespace tk #line 10 "Mathematics/garner.hpp" /** * @brief https://tkmst201.github.io/Library/Mathematics/garner.hpp */ namespace tk { template<typename T> bool pre_garner(std::vector<T> & b, std::vector<T> & m) noexcept { static_assert(std::is_integral<T>::value); static_assert(std::is_signed<T>::value); for (int i = 0; i < static_cast<int>(b.size()); ++i) { b[i] = b[i] % m[i] + (b[i] >= 0 ? 0 : m[i]); for (int j = 0; j < i; ++j) { T g = gcd(m[i], m[j]); if ((b[i] - b[j]) % g != 0) return false; m[i] /= g; m[j] /= g; T gi = gcd(g, m[i]), gj = g / gi; do { g = gcd(gi, gj); gi *= g; gj /= g; } while (g != 1); m[i] *= gi; m[j] *= gj; b[i] %= m[i]; b[j] %= m[j]; } } return true; } template<typename T, typename U> T garner(const std::vector<T> & b, const std::vector<T> & m, const T M) { static_assert(std::is_integral<T>::value); assert(b.size() == m.size()); const int n = b.size(); assert(n > 0); { T g = 0; for (auto v : m) { assert(v > 0); g = gcd(g, v); } assert(n == 1 || g == 1); } assert(M > 0); std::vector<T> sum(n + 1, 0), ip(n + 1, 1); for (int i = 0; i < n; ++i) { if (m[i] == 1) continue; U t = (b[i] % m[i] + static_cast<U>(2) * m[i] - sum[i]) % m[i] * mod_inv(ip[i], m[i]) % m[i]; for (int j = i + 1; j < n; ++j) { sum[j] = (sum[j] + ip[j] * t) % m[j]; ip[j] = static_cast<U>(ip[j]) * m[i] % m[j]; } sum[n] = (sum[n] + ip[n] * t % M) % M; ip[n] = static_cast<U>(ip[n]) * m[i] % M; } return sum.back(); } } // namespace tk #line 6 "Convolution/NumberTheoreticTransform_AnyMod.hpp" #line 11 "Convolution/NumberTheoreticTransform_AnyMod.hpp" /** * @brief https://tkmst201.github.io/Library/Convolution/NumberTheoreticTransform_AnyMod.hpp */ template<int MOD> struct NumberTheoreticTransform_AnyMod { static_assert(MOD > 0); private: using calc_type = long long; using uint32 = std::uint32_t; public: template<typename T> static std::vector<T> multiply(const std::vector<T> & a, const std::vector<T> & b) { static_assert(std::is_integral<T>::value); static_assert(std::is_signed<T>::value); for (uint32 i = 0; i < a.size(); ++i) assert(a[i] >= 0); for (uint32 i = 0; i < b.size(); ++i) assert(b[i] >= 0); std::vector<T> m; auto ntt1_res = NumberTheoreticTransform<1'224'736'769, 3>::multiply(a, b); m.emplace_back(1'224'736'769); auto ntt2_res = NumberTheoreticTransform<469'762'049, 3>::multiply(a, b); m.emplace_back(469'762'049); auto ntt3_res = NumberTheoreticTransform<167'772'161, 3>::multiply(a, b); m.emplace_back(167'772'161); // auto ntt4_res = NumberTheoreticTransform<998'244'353, 3>::multiply(a, b); // m.emplace_back(998'244'353); std::vector<T> c(m.size()); std::vector<T> res(ntt1_res.size()); for (uint32 i = 0; i < res.size(); ++i) { c[0] = ntt1_res[i]; c[1] = ntt2_res[i]; c[2] = ntt3_res[i]; // c[3] = ntt4_res[i]; res[i] = tk::garner<T, calc_type>(c, m, MOD); } return res; } }; #line 4 "Test/NumberTheoreticTransform_AnyMod.test.cpp" #include <cstdio> #line 7 "Test/NumberTheoreticTransform_AnyMod.test.cpp" int main() { int N, M; scanf("%d %d", &N, &M); std::vector<int> A(N), B(M); for (int i = 0; i < N; ++i) scanf("%d", &A[i]); for (int i = 0; i < M; ++i) scanf("%d", &B[i]); auto ans = NumberTheoreticTransform_AnyMod<1'000'000'007>::multiply(A, B); for (int i = 0; i < N + M - 1; ++i) printf("%d%c", ans[i], i == N + M - 1 ? '\n': ' '); }