#include using namespace std; using ll = long long; using ld = long double; using ull = unsigned long long; using pll = pair; using tlll = tuple; constexpr ll INF = 1LL << 60; template bool chmin(T& a, T b) {if (a > b) {a = b; return true;} return false;} template bool chmax(T& a, T b) {if (a < b) {a = b; return true;} return false;} ll safemod(ll a, ll m) {ll res = a % m; if (res < 0) res += m; return res;} ll divfloor(ll a, ll b) {if (b < 0) a = -a, b = -b; return (a - safemod(a, b)) / b;} ll divceil(ll a, ll b) {if (b < 0) a = -a, b = -b; return divfloor(a + b - 1, b);} ll ipow(ll a, ll b) {if (a == 0 || a == 1) {return a;} if (a == -1) {return b & 1 ? -1 : 1;} ll res = 1; for (int i = 0; i < b; i++) {res *= a;} return res;} ll mul_limited(ll a, ll b, ll m = INF) { return b == 0 ? 0 : a > m / b ? m : a * b; } ll pow_limited(ll a, ll b, ll m = INF) { if (a == 0 || a == 1) {return a;} ll res = 1; for (int i = 0; i < b; i++) {if (res > m / a) return m; res *= a;} return res;} //* ull lsb_pos(ull x) { assert(x != 0); return countr_zero(x); } ull msb_pos(ull x) { assert(x != 0); return bit_width(x) - 1; } ull lsb_mask(ull x) { assert(x != 0); return x & -x; } ull msb_mask(ull x) { assert(x != 0); return bit_floor(x); } //*/ template void unique(vector &v) {v.erase(unique(v.begin(), v.end()), v.end());} template void sortunique(vector &v) {sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());} void unique(string &s) {s.erase(unique(s.begin(), s.end()), s.end());} void sortunique(string &s) {sort(s.begin(), s.end()); s.erase(unique(s.begin(), s.end()), s.end());} #define FINALANS(x) do {cout << (x) << '\n'; exit(0);} while (false) //* #include using namespace atcoder; using mint = modint998244353; //using mint = modint1000000007; //using mint = modint; template * = nullptr> istream &operator>>(istream &is, T &a) { ll v; is >> v; a = v; return is; } template * = nullptr> ostream &operator<<(ostream &os, const T &a) { os << a.val(); return os; } //*/ #define rep(i, n) for (ll i = 0; i < ll(n); i++) #define rep2(i, l, r) for (ll i = ll(l); i < ll(r); i++) #define rep3(i, l, r, d) for (ll i = ll(l); (d) > 0 ? i < ll(r) : i > ll(r); i += d) template struct vector2 : vector> { using vector>::operator=; vector2(size_t n, size_t m, T val = 0) { *this = vector>(n, vector(m, val)); } }; template void PRINTVEC(const vector &v) {int n = v.size(); for (int i = 0; i < n; i++) cout << v[i] << (i == n - 1 ? "" : " "); cout << '\n';} template void PRINTVECT(const vector &v) {for (auto &vi : v) cout << vi << '\n';} template void PRINTVEC2(const vector> &v) {for (auto &vi : v) PRINTVEC(vi);} #ifdef LOCAL // ttps://zenn.dev/sassan/articles/19db660e4da0a4 #include "/Users/Shared/cpp-dump-main/dump.hpp" #define dump(...) cpp_dump(__VA_ARGS__) CPP_DUMP_DEFINE_DANGEROUS_EXPORT_OBJECT(val()); #else #define dump(...) #endif template class binom { public: vector fac, finv, inv; binom(int M) { fac.resize(M + 1); finv.resize(M + 1); inv.resize(M + 1); //* fac[0] = T(1); for (int i = 1; i <= M; i++) fac[i] = fac[i - 1] * T::raw(i); finv[M] = fac[M].inv(); for (int i = M - 1; i >= 0; i--) finv[i] = finv[i + 1] * T::raw(i + 1); for (int i = 1; i <= M; i++) inv[i] = fac[i - 1] * finv[i]; //*/ /* fac[0] = T(1), finv[0] = T(1); fac[1] = T(1), finv[1] = T(1), inv[1] = T(1); for (int i = 2; i <= M; i++) { fac[i] = fac[i - 1] * i; inv[i] = -inv[T::mod() % i] * (T::mod() / i); finv[i] = finv[i - 1] * inv[i]; } //*/ } T P(int N, int K) { if (N < K) return 0; if (N < 0 || K < 0) return 0; return fac[N] * finv[N - K]; } T C(int N, int K) { if (N < K) return 0; if (N < 0 || K < 0) return 0; return fac[N] * finv[K] * finv[N - K]; } T H(int N, int K) { if (N == 0 && K == 0) return 1; return C(N + K - 1, K); } }; // f(i) = ys[i] で定まる多項式 f(x) について f(c), …, f(c + M - 1) を求める template vector sample_points_shift(const vector &ys, int M, T c) { int N = ys.size(); binom bi(max(N, M)); vector a; { vector p(N), q(N); for (int i = 0; i < N; i++) { p[i] = ys[i] * bi.finv[i]; q[i] = i % 2 == 0 ? bi.finv[i] : -bi.finv[i]; } a = convolution(p, q); a.resize(N); } vector b; { vector p(N), q(N); T tmp = 1; for (int i = 0; i < N; i++) { p[i] = a[i] * bi.fac[i]; q[i] = tmp * bi.finv[i]; tmp *= c - i; } reverse(q.begin(), q.end()); b = convolution(p, q); b.erase(b.begin(), b.begin() + N - 1); for (int i = 0; i < N; i++) b[i] *= bi.finv[i]; } vector res; { vector p(M); for (int i = 0; i < M; i++) p[i] = bi.finv[i]; res = convolution(b, p); res.resize(M); for (int i = 0; i < M; i++) res[i] *= bi.fac[i]; } return res; } // https://suisen-kyopro.hatenablog.com/entry/2023/11/22/201600 // 前計算 O(K 2^K + (P/2^K) log K), クエリ O(2^K) template struct FactorialFast { private: const int P, K; vector Y, Z, fac; public: FactorialFast(const int K = 9) : P(T::mod()), K(K) { Y = {1}; for (int i = 0; i < K; i++) { Z = sample_points_shift(Y, (1 << (i + 2)) - (1 << i), 1 << i); Z.insert(Z.begin(), Y.begin(), Y.end()); Y.resize(1 << (i + 1)); for (int j = 0; j < (1 << (i + 1)); j++) Y[j] = Z[2 * j] * Z[2 * j + 1] * T::raw((1 << i) * (2 * j + 1)); } if ((1 << K) <= P / (1 << K)) { Z = sample_points_shift(Y, P / (1 << K), 1 << K); Y.insert(Y.end(), Z.begin(), Z.end()); } fac.resize(P / (1 << K) + 1); fac.at(0) = 1; for (int i = 0; i < P / (1 << K); i++) fac[i + 1] = fac[i] * Y[i] * T::raw((1 + i) * (1 << K)); } T query(ll n) { if (n >= T::mod()) return 0; T res = fac.at(n / (1 << K)); for (int j = n / (1 << K) * (1 << K) + 1; j <= n; j++) res *= T::raw(j); return res; } }; int main() { cin.tie(0); ios::sync_with_stdio(false); #ifdef LOCAL while (true) #endif { ll N, K; cin >> N >> K; if (K < N - K) K = N - K; FactorialFast fac; if (N < mint::mod()) { mint ans = fac.query(N) / (fac.query(K) * fac.query(N - K)); cout << ans << endl; } else { if (K < mint::mod()) cout << 0 << endl; else { mint ans = 1; rep(i, N - K) ans *= N - i; ans /= fac.query(N - K); cout << ans << endl; } } } }