結果
問題 | No.502 階乗を計算するだけ |
ユーザー | suisen |
提出日時 | 2024-01-30 21:10:33 |
言語 | C++17 (gcc 12.3.0 + boost 1.83.0) |
結果 |
AC
|
実行時間 | 770 ms / 1,000 ms |
コード長 | 12,959 bytes |
コンパイル時間 | 2,875 ms |
コンパイル使用メモリ | 132,800 KB |
実行使用メモリ | 80,768 KB |
最終ジャッジ日時 | 2024-09-28 10:17:30 |
合計ジャッジ時間 | 11,979 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge1 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 2 ms
6,812 KB |
testcase_01 | AC | 2 ms
6,944 KB |
testcase_02 | AC | 2 ms
6,944 KB |
testcase_03 | AC | 2 ms
6,944 KB |
testcase_04 | AC | 2 ms
6,940 KB |
testcase_05 | AC | 2 ms
6,940 KB |
testcase_06 | AC | 2 ms
6,944 KB |
testcase_07 | AC | 2 ms
6,940 KB |
testcase_08 | AC | 2 ms
6,944 KB |
testcase_09 | AC | 2 ms
6,944 KB |
testcase_10 | AC | 2 ms
6,940 KB |
testcase_11 | AC | 2 ms
6,944 KB |
testcase_12 | AC | 2 ms
6,940 KB |
testcase_13 | AC | 2 ms
6,940 KB |
testcase_14 | AC | 2 ms
6,944 KB |
testcase_15 | AC | 2 ms
6,940 KB |
testcase_16 | AC | 2 ms
6,940 KB |
testcase_17 | AC | 2 ms
6,940 KB |
testcase_18 | AC | 2 ms
6,940 KB |
testcase_19 | AC | 2 ms
6,940 KB |
testcase_20 | AC | 2 ms
6,940 KB |
testcase_21 | AC | 2 ms
6,944 KB |
testcase_22 | AC | 15 ms
10,560 KB |
testcase_23 | AC | 7 ms
6,940 KB |
testcase_24 | AC | 11 ms
8,092 KB |
testcase_25 | AC | 3 ms
6,940 KB |
testcase_26 | AC | 8 ms
6,940 KB |
testcase_27 | AC | 6 ms
6,940 KB |
testcase_28 | AC | 7 ms
6,940 KB |
testcase_29 | AC | 4 ms
6,940 KB |
testcase_30 | AC | 15 ms
10,084 KB |
testcase_31 | AC | 9 ms
6,940 KB |
testcase_32 | AC | 765 ms
80,640 KB |
testcase_33 | AC | 762 ms
80,680 KB |
testcase_34 | AC | 760 ms
80,760 KB |
testcase_35 | AC | 770 ms
80,636 KB |
testcase_36 | AC | 762 ms
80,728 KB |
testcase_37 | AC | 759 ms
80,768 KB |
testcase_38 | AC | 757 ms
80,768 KB |
testcase_39 | AC | 763 ms
80,636 KB |
testcase_40 | AC | 763 ms
80,636 KB |
testcase_41 | AC | 765 ms
80,764 KB |
testcase_42 | AC | 2 ms
5,376 KB |
testcase_43 | AC | 2 ms
5,376 KB |
testcase_44 | AC | 2 ms
5,376 KB |
testcase_45 | AC | 2 ms
5,376 KB |
testcase_46 | AC | 2 ms
5,376 KB |
testcase_47 | AC | 2 ms
5,376 KB |
testcase_48 | AC | 2 ms
5,376 KB |
testcase_49 | AC | 2 ms
5,376 KB |
testcase_50 | AC | 2 ms
5,376 KB |
testcase_51 | AC | 2 ms
5,376 KB |
ソースコード
#define PROBLEM "https://yukicoder.me/problems/no/502" #include <iostream> #include <atcoder/modint> using mint = atcoder::modint1000000007; #include <utility> #include <vector> #include <atcoder/convolution> #include <cassert> namespace suisen { template <typename T, typename U = T> struct factorial { factorial() = default; factorial(int n) { ensure(n); } static void ensure(const int n) { int sz = _fac.size(); if (n + 1 <= sz) return; int new_size = std::max(n + 1, sz * 2); _fac.resize(new_size), _fac_inv.resize(new_size); for (int i = sz; i < new_size; ++i) _fac[i] = _fac[i - 1] * i; _fac_inv[new_size - 1] = U(1) / _fac[new_size - 1]; for (int i = new_size - 1; i > sz; --i) _fac_inv[i - 1] = _fac_inv[i] * i; } T fac(const int i) { ensure(i); return _fac[i]; } T operator()(int i) { return fac(i); } U fac_inv(const int i) { ensure(i); return _fac_inv[i]; } U binom(const int n, const int r) { if (n < 0 or r < 0 or n < r) return 0; ensure(n); return _fac[n] * _fac_inv[r] * _fac_inv[n - r]; } template <typename ...Ds, std::enable_if_t<std::conjunction_v<std::is_integral<Ds>...>, std::nullptr_t> = nullptr> U polynom(const int n, const Ds& ...ds) { if (n < 0) return 0; ensure(n); int sumd = 0; U res = _fac[n]; for (int d : { ds... }) { if (d < 0 or d > n) return 0; sumd += d; res *= _fac_inv[d]; } if (sumd > n) return 0; res *= _fac_inv[n - sumd]; return res; } U perm(const int n, const int r) { if (n < 0 or r < 0 or n < r) return 0; ensure(n); return _fac[n] * _fac_inv[n - r]; } private: static std::vector<T> _fac; static std::vector<U> _fac_inv; }; template <typename T, typename U> std::vector<T> factorial<T, U>::_fac{ 1 }; template <typename T, typename U> std::vector<U> factorial<T, U>::_fac_inv{ 1 }; } // namespace suisen namespace suisen { template <typename mint, typename Convolve, std::enable_if_t<std::is_invocable_r_v<std::vector<mint>, Convolve, std::vector<mint>, std::vector<mint>>, std::nullptr_t> = nullptr> std::vector<mint> shift_of_sampling_points(const std::vector<mint>& ys, mint t, int m, const Convolve &convolve) { const int n = ys.size(); factorial<mint> fac(std::max(n, m)); std::vector<mint> b = [&] { std::vector<mint> f(n), g(n); for (int i = 0; i < n; ++i) { f[i] = ys[i] * fac.fac_inv(i); g[i] = (i & 1 ? -1 : 1) * fac.fac_inv(i); } std::vector<mint> b = convolve(f, g); b.resize(n); return b; }(); std::vector<mint> e = [&] { std::vector<mint> c(n); mint prd = 1; std::reverse(b.begin(), b.end()); for (int i = 0; i < n; ++i) { b[i] *= fac.fac(n - i - 1); c[i] = prd * fac.fac_inv(i); prd *= t - i; } std::vector<mint> e = convolve(b, c); e.resize(n); return e; }(); std::reverse(e.begin(), e.end()); for (int i = 0; i < n; ++i) { e[i] *= fac.fac_inv(i); } std::vector<mint> f(m); for (int i = 0; i < m; ++i) f[i] = fac.fac_inv(i); std::vector<mint> res = convolve(e, f); res.resize(m); for (int i = 0; i < m; ++i) res[i] *= fac.fac(i); return res; } template <typename mint> std::vector<mint> shift_of_sampling_points(const std::vector<mint>& ys, mint t, int m) { auto convolve = [&](const std::vector<mint> &f, const std::vector<mint> &g) { return atcoder::convolution(f, g); }; return shift_of_sampling_points(ys, t, m, convolve); } } // namespace suisen #include <atcoder/convolution> #include <iostream> namespace suisen::internal { template <typename T, typename R = T> std::vector<R> convolution_naive(const std::vector<T>& a, const std::vector<T>& b) { const int n = a.size(), m = b.size(); std::vector<R> c(n + m - 1); if (n < m) { for (int j = 0; j < m; j++) for (int i = 0; i < n; i++) c[i + j] += R(a[i]) * b[j]; } else { for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) c[i + j] += R(a[i]) * b[j]; } return c; } } // namespace suisen namespace suisen { template <typename mint, atcoder::internal::is_modint_t<mint>* = nullptr> std::vector<mint> arbitrary_mod_convolution(const std::vector<mint>& a, const std::vector<mint>& b) { int n = int(a.size()), m = int(b.size()); if constexpr (atcoder::internal::is_static_modint<mint>::value) { int maxz = 1; while (not ((mint::mod() - 1) & maxz)) maxz <<= 1; int z = 1; while (z < n + m - 1) z <<= 1; if (z <= maxz) return atcoder::convolution<mint>(a, b); } if (n == 0 or m == 0) return {}; if (std::min(n, m) <= 120) return internal::convolution_naive(a, b); static constexpr long long MOD1 = 754974721; // 2^24 static constexpr long long MOD2 = 167772161; // 2^25 static constexpr long long MOD3 = 469762049; // 2^26 static constexpr long long M1M2 = MOD1 * MOD2; static constexpr long long INV_M1_MOD2 = atcoder::internal::inv_gcd(MOD1, MOD2).second; static constexpr long long INV_M1M2_MOD3 = atcoder::internal::inv_gcd(M1M2, MOD3).second; std::vector<int> a2(n), b2(m); for (int i = 0; i < n; ++i) a2[i] = a[i].val(); for (int i = 0; i < m; ++i) b2[i] = b[i].val(); auto c1 = atcoder::convolution<MOD1>(a2, b2); auto c2 = atcoder::convolution<MOD2>(a2, b2); auto c3 = atcoder::convolution<MOD3>(a2, b2); const long long m1m2 = mint(M1M2).val(); std::vector<mint> c(n + m - 1); for (int i = 0; i < n + m - 1; ++i) { // Garner's Algorithm // X = x1 + x2 * m1 + x3 * m1 * m2 // x1 = c1[i], x2 = (c2[i] - x1) / m1 (mod m2), x3 = (c3[i] - x1 - x2 * m1) / m2 (mod m3) long long x1 = c1[i]; long long x2 = (atcoder::static_modint<MOD2>(c2[i] - x1) * INV_M1_MOD2).val(); long long x3 = (atcoder::static_modint<MOD3>(c3[i] - x1 - x2 * MOD1) * INV_M1M2_MOD3).val(); c[i] = x1 + x2 * MOD1 + x3 * m1m2; } return c; } std::vector<__uint128_t> convolution_int(const std::vector<int> &a, const std::vector<int> &b) { int n = int(a.size()), m = int(b.size()); auto check_nonnegative = [](int e) { return e >= 0; }; assert(std::all_of(a.begin(), a.end(), check_nonnegative)); assert(std::all_of(b.begin(), b.end(), check_nonnegative)); if (n == 0 or m == 0) return {}; if (std::min(n, m) <= 120) return internal::convolution_naive<int, __uint128_t>(a, b); static constexpr long long MOD1 = 754974721; // 2^24 static constexpr long long MOD2 = 167772161; // 2^25 static constexpr long long MOD3 = 469762049; // 2^26 static constexpr long long M1M2 = MOD1 * MOD2; static constexpr long long INV_M1_MOD2 = atcoder::internal::inv_gcd(MOD1, MOD2).second; static constexpr long long INV_M1M2_MOD3 = atcoder::internal::inv_gcd(M1M2, MOD3).second; auto c1 = atcoder::convolution<MOD1>(a, b); auto c2 = atcoder::convolution<MOD2>(a, b); auto c3 = atcoder::convolution<MOD3>(a, b); std::vector<__uint128_t> c(n + m - 1); for (int i = 0; i < n + m - 1; ++i) { // Garner's Algorithm // X = x1 + x2 * m1 + x3 * m1 * m2 // x1 = c1[i], x2 = (c2[i] - x1) / m1 (mod m2), x3 = (c3[i] - x1 - x2 * m1) / m2 (mod m3) int x1 = c1[i]; int x2 = (atcoder::static_modint<MOD2>(c2[i] - x1) * INV_M1_MOD2).val(); int x3 = (atcoder::static_modint<MOD3>(c3[i] - x1 - x2 * MOD1) * INV_M1M2_MOD3).val(); c[i] = x1 + x2 * MOD1 + __uint128_t(x3) * M1M2; } return c; } } // namespace suisen namespace suisen { // mod must be a prime number template <typename mint, std::enable_if_t<atcoder::internal::is_static_modint<mint>::value, std::nullptr_t> = nullptr> struct factorial_large { using value_type = mint; static constexpr int LOG_BLOCK_SIZE = 9; static constexpr int BLOCK_SIZE = 1 << LOG_BLOCK_SIZE; static constexpr int BLOCK_NUM = value_type::mod() >> LOG_BLOCK_SIZE; static inline int threshold = 2000000; static_assert(atcoder::internal::is_prime_constexpr(mint::mod())); static value_type fac(long long n) { if (n >= mint::mod()) return 0; return n <= threshold ? factorial<mint>{}.fac(n) : _large_fac(n); } static value_type fac_inv(long long n) { assert(n < (long long) mint::mod()); return n <= threshold ? factorial<mint>{}.fac_inv(n) : _large_fac(n).inv(); } static value_type binom(int n, int r) { if (r < 0 or r > n) return 0; return fac(n) * fac_inv(r) * fac_inv(n - r); } template <typename ...Ds, std::enable_if_t<std::conjunction_v<std::is_integral<Ds>...>, std::nullptr_t> = nullptr> static value_type polynom(const int n, const Ds& ...ds) { if (n < 0) return 0; long long sumd = 0; value_type res = fac(n); for (int d : { ds... }) { if (d < 0 or d > n) return 0; sumd += d; res *= fac_inv(d); } if (sumd > n) return 0; res *= fac_inv(n - sumd); return res; } static value_type perm(int n, int r) { if (r < 0 or r > n) return 0; return fac(n) * fac_inv(n - r); } private: static inline std::vector<value_type> _block_fac{}; static void _build() { if (_block_fac.size()) { return; } std::vector<value_type> f{ 1 }; f.reserve(BLOCK_SIZE); for (int i = 0; i < LOG_BLOCK_SIZE; ++i) { std::vector<value_type> g = shift_of_sampling_points<value_type>(f, 1 << i, 3 << i, arbitrary_mod_convolution<value_type>); const auto get = [&](int j) { return j < (1 << i) ? f[j] : g[j - (1 << i)]; }; f.resize(2 << i); for (int j = 0; j < 2 << i; ++j) { f[j] = get(2 * j) * get(2 * j + 1) * ((2 * j + 1) << i); } } // f_B(x) = (x+1) * ... * (x+B-1) if (BLOCK_NUM > BLOCK_SIZE) { std::vector<value_type> g = shift_of_sampling_points<value_type>(f, BLOCK_SIZE, BLOCK_NUM - BLOCK_SIZE, arbitrary_mod_convolution<value_type>); std::move(g.begin(), g.end(), std::back_inserter(f)); } else { f.resize(BLOCK_NUM); } for (int i = 0; i < BLOCK_NUM; ++i) { f[i] *= value_type(i + 1) * BLOCK_SIZE; } // f[i] = (i*B + 1) * ... * (i*B + B) f.insert(f.begin(), 1); for (int i = 1; i <= BLOCK_NUM; ++i) { f[i] *= f[i - 1]; } _block_fac = std::move(f); } static value_type _large_fac(int n) { _build(); value_type res; int q = n / BLOCK_SIZE, r = n % BLOCK_SIZE; if (2 * r <= BLOCK_SIZE) { res = _block_fac[q]; for (int i = 0; i < r; ++i) { res *= value_type::raw(n - i); } } else if (q != factorial_large::BLOCK_NUM) { res = _block_fac[q + 1]; value_type den = 1; for (int i = 1; i <= BLOCK_SIZE - r; ++i) { den *= value_type::raw(n + i); } res /= den; } else { // Wilson's theorem res = value_type::mod() - 1; value_type den = 1; for (int i = value_type::mod() - 1; i > n; --i) { den *= value_type::raw(i); } res /= den; } return res; } }; } // namespace suisen int main() { long long n; std::cin >> n; std::cout << suisen::factorial_large<mint>{}.fac(n).val() << '\n'; }