#define PROBLEM "https://judge.yosupo.jp/problem/factorial" #include #include using mint = atcoder::modint1000000007; #include #include #include #include namespace suisen { template 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]; } 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 _fac; static std::vector _fac_inv; }; template std::vector factorial::_fac{ 1 }; template std::vector factorial::_fac_inv{ 1 }; } // namespace suisen namespace suisen { template , Convolve, std::vector, std::vector>, std::nullptr_t> = nullptr> std::vector shift_of_sampling_points(const std::vector& ys, mint t, int m, const Convolve &convolve) { const int n = ys.size(); factorial fac(std::max(n, m)); std::vector b = [&] { std::vector 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 b = convolve(f, g); b.resize(n); return b; }(); std::vector e = [&] { std::vector 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 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 f(m); for (int i = 0; i < m; ++i) f[i] = fac.fac_inv(i); std::vector res = convolve(e, f); res.resize(m); for (int i = 0; i < m; ++i) res[i] *= fac.fac(i); return res; } template std::vector shift_of_sampling_points(const std::vector& ys, mint t, int m) { auto convolve = [&](const std::vector &f, const std::vector &g) { return atcoder::convolution(f, g); }; return shift_of_sampling_points(ys, t, m, convolve); } } // namespace suisen #include #include namespace suisen::internal { template std::vector convolution_naive(const std::vector& a, const std::vector& b) { const int n = a.size(), m = b.size(); std::vector 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 * = nullptr> std::vector arbitrary_mod_convolution(const std::vector& a, const std::vector& b) { int n = int(a.size()), m = int(b.size()); if constexpr (atcoder::internal::is_static_modint::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(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 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(a2, b2); auto c2 = atcoder::convolution(a2, b2); auto c3 = atcoder::convolution(a2, b2); const long long m1m2 = mint(M1M2).val(); std::vector 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(c2[i] - x1) * INV_M1_MOD2).val(); long long x3 = (atcoder::static_modint(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 &a, const std::vector &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(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(a, b); auto c2 = atcoder::convolution(a, b); auto c3 = atcoder::convolution(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(c2[i] - x1) * INV_M1_MOD2).val(); int x3 = (atcoder::static_modint(c3[i] - x1 - x2 * MOD1) * INV_M1M2_MOD3).val(); c[i] = x1 + x2 * MOD1 + __uint128_t(x3) * M1M2; } return c; } } // namespace suisen namespace suisen { template * = nullptr> struct FactorialLargePrimeMod { private: static constexpr int _p = mint::mod(); static constexpr int _log_b = 15; static constexpr int _b = 1 << _log_b; static constexpr int _q = _p >> _log_b; public: static constexpr int block_size = _b; static constexpr int log_block_size = _log_b; static constexpr int block_num = _q; FactorialLargePrimeMod() = delete; static mint fac(long long n) { if (_p <= n) return 0; build(); const int q = n >> _log_b, r = n & (_b - 1); // n! = (qb)! * (n-r+1)(n-r+2)...(n) mint ans = _block_fact[q]; for (int j = 0; j < r; ++j) { ans *= mint::raw(n - j); } return ans; } private: static inline std::vector _block_fact{}; static inline bool _built = false; static void build() { if (std::exchange(_built, true)) return; const auto convolve = [&](const std::vector &f, const std::vector &g) { return arbitrary_mod_convolution(f, g); }; // f_d(x) := (dx+1)*...*(dx+d-1) // Suppose that we have f_d(0),...,f_d(d-1). (Note that (deg f_d)+1=d) // f_{2d}(x) = ((2dx+1)*...*(2dx+d-1)) * (2dx+d) * (((2dx+d)+1)* ...*((2dx+d)+d-1)) // = f_d(2x) * f_d(2x+1) * (2dx+d) // We can calculate f_{2d}(0), ..., f_{2d}(2d-1) from f_d(0), f_d(1), ..., f_d(4d-2), f_d(4d-1) std::vector f{ 1 }; f.reserve(_b); for (int i = 0; i < _log_b; ++i) { std::vector g = shift_of_sampling_points(f, 1 << i, 3 << i, convolve); 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) { // (2*j+1)*2^i <= 2^(2*_log_b) + 2^(_log_b-1) < 2^31 holds if _log_b <= 15 f[j] = get(2 * j) * get(2 * j + 1) * ((2 * j + 1) << i); } } // f_B(x) = (x+1) * ... * (x+B-1) if (_q > _b) { std::vector g = shift_of_sampling_points(f, _b, _q - _b, convolve); std::move(g.begin(), g.end(), std::back_inserter(f)); } else { f.resize(_q); } for (int i = 0; i < _q; ++i) { f[i] *= mint(i + 1) * _b; } // f[i] = (i*B + 1) * ... * (i*B + B) _block_fact = std::move(f); _block_fact.insert(_block_fact.begin(), 1); for (int i = 1; i <= _q; ++i) { _block_fact[i] *= _block_fact[i - 1]; } } }; } // namespace suisen int main() { using Factorial = suisen::FactorialLargePrimeMod; int n; std::cin >> n; std::cout << Factorial::fac(n).val() << '\n'; }