// Exported by Exporter.exe // Included from C.cpp // Compile flags -Wall -Wextra -Wshadow -D_GLIBCXX_ASSERTIONS -DDEBUG -ggdb3 -fmax-errors=2 #include using namespace std; #define PB push_back #define F first #define S second #define MP make_pair #define MTP make_tuple #define R Read #define RD Read_Digit #define RP Read_P #define RL Read_Loop #define RLD Read_Loop_Digit #define RLP Read_Loop_P typedef long long int ll; typedef unsigned long long int ull; constexpr int kN = 1 << 25; constexpr int kMod = 998244353; // constexpr int kMod = int(1E9 + 7); // constexpr int kInf = 0x3f3f3f3f; // constexpr ll kInf = 0x3f3f3f3f3f3f3f3f; // constexpr double kPi = acos(-1); // constexpr double kEps = 1E-9; // Included from C:\Users\ianli\Desktop\CP\template\Various\Fast_IO\Fast_IO.cpp // --- Get --- static inline char Get_Raw_Char() { static char buf[1 << 16], *p = buf, *end = buf; if (p == end) { if ((end = buf + fread(buf, 1, 1 << 16, stdin)) == buf) return '\0'; p = buf; } return *p++; } static inline int Get_Digit() { char c = Get_Raw_Char(); while (!isdigit(c)) c = Get_Raw_Char(); return int(c - '0'); } template static inline int Get_P() { static_assert(is_integral::value); char c = Get_Raw_Char(); while (!isdigit(c)) c = Get_Raw_Char(); T ret = int(c - '0'); while (isdigit(c = Get_Raw_Char())) ret = ret * 10 + int(c - '0'); return ret; } template static inline int Get() { static_assert(is_integral::value); char c = Get_Raw_Char(); bool neg = false; while (!isdigit(c)) { if (c == '-') neg = true; c = Get_Raw_Char(); } T ret = int(c - '0'); while (isdigit(c = Get_Raw_Char())) ret = ret * 10 + int(c - '0'); if (neg) return -ret; return ret; } // --- Read --- template static inline void Read_P(T &n) { static_assert(is_integral::value); char c = Get_Raw_Char(); while (!isdigit(c)) c = Get_Raw_Char(); n = int(c - '0'); while (isdigit(c = Get_Raw_Char())) n = n * 10 + int(c - '0'); return ; } template static inline void Read(T &n) { static_assert(is_integral::value); char c = Get_Raw_Char(); bool neg = false; while (!isdigit(c)) { if (c == '-') neg = true; c = Get_Raw_Char(); } n = int(c - '0'); while (isdigit(c = Get_Raw_Char())) n = n * 10 + int(c - '0'); if (neg) n = -n; return ; } template static inline void Read_Digit(T &n) { static_assert(is_integral::value); char c = Get_Raw_Char(); while (!isdigit(c)) c = Get_Raw_Char(); n = int(c - '0'); return ; } // --- Read multiple --- template static inline void Read(T &n, Targs&... Fargs) { Read(n); return Read(Fargs...); } template static inline void Read_Digit(T &n, Targs&... Fargs) { Read_Digit(n); return Read_Digit(Fargs...); } template static inline void Read_P(T &n, Targs&... Fargs) { Read_P(n); return Read_P(Fargs...); } // --- Read Loop --- template static inline void Read_Loop_i(int i, T *a) {return Read(a[i]);} template static inline void Read_Loop_i(int i, T *a, Targs*... Fargs) { Read(a[i]); return Read_Loop_i(i, Fargs...); } template static inline void Read_Loop(int n, Targs*... Fargs) { for (int i = 1; i <= n; i++) Read_Loop_i(i, Fargs...); return ; } template static inline void Read_Loop_Digit_i(int i, T *a) {return Read_Digit(a[i]);} template static inline void Read_Loop_Digit_i(int i, T *a, Targs*... Fargs) { Read_Digit(a[i]); return Read_Loop_Digit_i(i, Fargs...); } template static inline void Read_Loop_Digit(int n, Targs*... Fargs) { for (int i = 1; i <= n; i++) Read_Loop_Digit_i(i, Fargs...); return ; } template static inline void Read_Loop_P_i(int i, T *a) {return Read_P(a[i]);} template static inline void Read_Loop_P_i(int i, T *a, Targs*... Fargs) { Read_P(a[i]); return Read_Loop_P_i(i, Fargs...); } template static inline void Read_Loop_P(int n, Targs*... Fargs) { for (int i = 1; i <= n; i++) Read_Loop_P_i(i, Fargs...); return ; } // --- Float --- template static inline void Read(T &n) { char c = Get_Raw_Char(); bool neg = false; while (!isdigit(c)) { if (c == '-') neg = true; c = Get_Raw_Char(); } n = int(c - '0'); while (isdigit(c = Get_Raw_Char())) n = n * 10 + int(c - '0'); int cnt = 0; if (c == '.') { while (isdigit(c = Get_Raw_Char())) { n = n * 10 + int(c - '0'); cnt++; } } while (cnt < mul) { n = n * 10; cnt++; } if (neg) n = -n; return ; } template static inline void Read_P(T &n) { char c = Get_Raw_Char(); while (!isdigit(c)) c = Get_Raw_Char(); n = int(c - '0'); while (isdigit(c = Get_Raw_Char())) n = n * 10 + int(c - '0'); int cnt = 0; if (c == '.') { while (isdigit(c = Get_Raw_Char())) { n = n * 10 + int(c - '0'); cnt++; } } while (cnt < mul) { n = n * 10; cnt++; } return ; } template static inline void Read(T &n, Targs&... Fargs) { Read(n); return Read(Fargs...); } template static inline void Read_P(T &n, Targs&... Fargs) { Read_P(n); return Read_P(Fargs...); } // --- cin, cout --- void IOS() { ios::sync_with_stdio(false); cin.tie(0); return ; } // --- freopen --- void Freopen(const char *in, const char *out) { freopen(in, "r", stdin); freopen(out, "w", stdout); return ; } // --- Output __int128 --- template void Print128(T x) { if (x < 0) { printf("-"); x = -x; } if (x == 0) printf("0"); else { static int val[100]; int idx = -1; while (x) { val[++idx] = x % 10; x /= 10; } while (idx >= 0) printf("%d", val[idx--]); } } // End of C:\Users\ianli\Desktop\CP\template\Various\Fast_IO\Fast_IO.cpp // Included from C:\Users\ianli\Desktop\CP\template\Various\Useful_Functions\Useful_Functions.cpp // --- sort --- template inline void sort(vector &v) {return sort(v.begin(), v.end());} template inline void sort(int n, T *a) {return sort(a + 1, a + n + 1);} template inline void sort_r(vector &v) {return sort(v.begin(), v.end(), greater());} template inline void sort_r(int n, T *a) {return sort(a + 1, a + n + 1, greater());} // --- Merge --- template inline void Merge_Vec(vector &a, vector &b, vector &c) { if (c.size() < a.size() + b.size()) c.resize(a.size() + b.size()); merge(a.begin(), a.end(), b.begin(), b.end(), c.begin()); return ; } // --- Discrete --- template inline void Discrete(vector &v) { sort(v); v.resize(unique(v.begin(), v.end()) - v.begin()); return ; } // --- Relabel --- template inline void relabel(int n, T *val, T *dist) { if (!dist) dist = val; T *tmp = new T[n + 1]; memcpy(tmp, val, sizeof(T) * (n + 1)); sort(n, tmp); int sz = unique(tmp + 1, tmp + n + 1) - (tmp + 1); for (int i = 1; i <= n; i++) dist[i] = lower_bound(tmp + 1, tmp + sz + 1, val[i]) - tmp; delete tmp; return ; } // --- PQ --- template using PQ = priority_queue; template using PQ_R = priority_queue, greater>; // --- misc --- template inline T ABS(T n) {return n >= 0 ? n : -n;} template inline T gcd(T a, T b) {return b ? gcd(b, a % b) : a;} template inline T lcm(T a, T b) {return a * b / gcd(a, b);} template inline T mex(T a, T b) {return (a == 0 || b == 0) ? ((a == 1 || b == 1) ? 2 : 1) : 0;} template inline void chmin(T &a, T b) { a = min(a, b); return ; } template inline void chmax(T &a, T b) { a = max(a, b); return ; } // End of C:\Users\ianli\Desktop\CP\template\Various\Useful_Functions\Useful_Functions.cpp // Included from C:\Users\ianli\Desktop\CP\template\Various\Debug\Debug.cpp void print(const int &x) {printf("%d", x);} void print(const long long int &x) {printf("%lld", x);} void print(const double &x) {printf("%.20lf", x);} void print(const char *x) {printf("%s", x);} void print(const int n, const int *x) { printf("%d", x[1]); for (int i = 2; i <= n; i++) printf(" %d", x[i]); printf("\n"); return ; } void print(const int n, const long long int *x) { printf("%lld", x[1]); for (int i = 2; i <= n; i++) printf(" %lld", x[i]); printf("\n"); return ; } // End of C:\Users\ianli\Desktop\CP\template\Various\Debug\Debug.cpp // Included from C:\Users\ianli\Desktop\CP\template\Math\Mod_Int\Mod_Int.cpp template T Pow(T a, long long int b) { T ans(1); while (b) { if (b & 1) ans *= a; a *= a; b >>= 1; } return ans; } template struct Mod_Int { constexpr static int Mod() {return kMod;} long long int val; Mod_Int() {val = 0;} template Mod_Int(T x) {val = x;} template Mod_Int(Mod_Int x) {val = x.val % kMod;} Mod_Int inv() const {return Pow(*this, kMod - 2);} Mod_Int operator + (const Mod_Int &x) const { Mod_Int ans(val + x.val); if (ans.val >= kMod) ans.val -= kMod; return ans; } Mod_Int operator - (const Mod_Int &x) const { Mod_Int ans(val - x.val); if (ans.val < 0) ans.val += kMod; return ans; } Mod_Int operator * (const Mod_Int &x) const {return Mod_Int(val * x.val % kMod);} Mod_Int operator / (const Mod_Int &x) const {return *this * x.inv();} Mod_Int operator ^ (const Mod_Int &x) const {return Pow(*this, x.val);} Mod_Int operator << (const int &x) const {return (val << x) % kMod;} Mod_Int operator += (const Mod_Int &x) { val += x.val; if (val >= kMod) val -= kMod; return *this; } Mod_Int operator -= (const Mod_Int &x) { val -= x.val; if (val < 0) val += kMod; return *this; } Mod_Int operator *= (const Mod_Int &x) { val = val * x.val % kMod; return *this; } Mod_Int operator /= (const Mod_Int &x) { val = val * x.inv().val % kMod; return *this; } Mod_Int operator ^= (const Mod_Int &x) { val = Pow(*this, x.val).val; return *this; } Mod_Int operator <<= (const int &x) { val = (val << x) % kMod; return *this; } Mod_Int operator ++(int) { val++; if (val >= kMod) val -= kMod; return *this; } Mod_Int operator --(int) { val--; if (val < 0) val += kMod; return *this; } bool operator == (const Mod_Int &x) const {return val == x.val;} bool operator != (const Mod_Int &x) const {return val != x.val;} }; using Mint = Mod_Int; namespace Factorial { Mint *f, *inf; bool preprocessed_factorial; void Pre_Factorial(const int &sz) { if (preprocessed_factorial) return ; preprocessed_factorial = true; f = new Mint[sz + 1]; inf = new Mint[sz + 1]; f[0] = f[1] = inf[0] = inf[1] = 1; for (int i = 2; i <= sz; i++) f[i] = f[i - 1] * i; inf[sz] = f[sz].inv(); for (int i = sz; i > 2; i--) inf[i - 1] = inf[i] * i; return ; } inline Mint P(const int &n, const int &m) {return f[n] * inf[m];} inline Mint C(const int &n, const int &m) {return f[n] * inf[m] * inf[n - m];} inline Mint H(const int &n, const int &m) {return f[n + m - 1] * inf[m] * inf[n - 1];} inline Mint Catalan(const int &n) {return f[n << 1] * inf[n] * inf[n + 1];} } namespace Factorial_No_Inf { Mint *f; void Pre_Factorial(const int &sz) { f = new Mint[sz + 1]; f[0] = f[1] = 1; for (int i = 2; i <= sz; i++) f[i] = f[i - 1] * i; return ; } inline Mint P(const int &n, const int &m) {return f[n] / f[m];} inline Mint C(const int &n, const int &m) {return f[n] / (f[m] * f[n - m]);} inline Mint H(const int &n, const int &m) {return f[n + m - 1] / (f[m] * f[n - 1]);} inline Mint Catalan(const int &n) {return f[n << 1] / (f[n] * f[n + 1]);} } namespace Inverse { using namespace Factorial; Mint *inv; void Pre_Inverse(const int &sz) { inv = new Mint[sz + 1]; inv[1] = 1; Pre_Factorial(sz); for (int i = 1; i <= sz; i++) inv[i] = f[i - 1] * inf[i]; return ; } }; // End of C:\Users\ianli\Desktop\CP\template\Math\Mod_Int\Mod_Int.cpp // Included from C:\Users\ianli\Desktop\CP\template\Math\NTT\Butterfly.cpp namespace butterfly_inner { int bsf(unsigned int n) { #ifdef _MSC_VER unsigned long index; _BitScanForward(&index, n); return index; #else return __builtin_ctz(n); #endif } } int NTT_size(int size) {return 1 << (__lg(size) + 2);} template void NTT(T *v, int size) { assert(size == (size & -size)); static constexpr int g = 3; // primitive root int h = __lg(size); static bool first = true; static T sum_e[30]; if (first) { first = false; T es[30], ies[30]; int cnt2 = butterfly_inner::bsf(T::Mod() - 1); T e = Pow(T(g), (T::Mod() - 1) >> cnt2), ie = e.inv(); for (int i = cnt2; i >= 2; i--) { es[i - 2] = e; ies[i - 2] = ie; e *= e; ie *= ie; } T now = 1; for (int i = 0; i <= cnt2 - 2; i++) { sum_e[i] = es[i] * now; now *= ies[i]; } } for (int ph = 1; ph <= h; ph++) { int w = 1 << (ph - 1), p = 1 << (h - ph); T now = 1; for (int s = 0; s < w; s++) { int offset = s << (h - ph + 1); for (int i = 0; i < p; i++) { auto l = v[i + offset]; auto r = v[i + offset + p] * now; v[i + offset] = l + r; v[i + offset + p] = l - r; } now *= sum_e[butterfly_inner::bsf(~(unsigned int)(s))]; } } return ; } template void NTT_Inv(T *v, int size) { assert(size == (size & -size)); static constexpr int g = 3; // primitive root int h = __lg(size); static bool first = true; static T sum_ie[30]; if (first) { first = false; T es[30], ies[30]; int cnt2 = butterfly_inner::bsf(T::Mod() - 1); T e = Pow(T(g), (T::Mod() - 1) >> cnt2), ie = e.inv(); for (int i = cnt2; i >= 2; i--) { es[i - 2] = e; ies[i - 2] = ie; e *= e; ie *= ie; } T now = 1; for (int i = 0; i <= cnt2 - 2; i++) { sum_ie[i] = ies[i] * now; now *= es[i]; } } for (int ph = h; ph >= 1; ph--) { int w = 1 << (ph - 1), p = 1 << (h - ph); T inow = 1; for (int s = 0; s < w; s++) { int offset = s << (h - ph + 1); for (int i = 0; i < p; i++) { T l = v[i + offset]; T r = v[i + offset + p]; v[i + offset] = l + r; v[i + offset + p] = (l - r) * inow; } inow *= sum_ie[butterfly_inner::bsf(~(unsigned int)(s))]; } } T iz = T(size).inv(); for (int i = 0; i < size; i++) v[i] *= iz; return ; } // End of C:\Users\ianli\Desktop\CP\template\Math\NTT\Butterfly.cpp using namespace Factorial; Mint A[kN], B[kN]; int main() { int n, m, k; RP(n, m, k); Pre_Factorial(n); int ntt_sz = NTT_size(m); for (int i = 0; i * k <= m && i <= n + 1 - m; i++) { A[i * k] = C(n + 1 - m, i); if (i & 1) A[i * k] = Mint(0) - A[i * k]; } for (int i = 0; i <= m; i++) B[i] = C(i + (n - m), n - m); //for (int i = 0; i <= m; i++) printf("A[%d] = %lld\n", i, A[i]); //for (int i = 0; i <= m; i++) printf("B[%d] = %lld\n", i, B[i]); NTT(A, ntt_sz); NTT(B, ntt_sz); for (int i = 0; i < ntt_sz; i++) A[i] *= B[i]; NTT_Inv(A, ntt_sz); Mint ans; ans = C(n, m) - A[m]; printf("%lld\n", ans); } // End of C.cpp