import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Array; import java.math.BigInteger; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Map; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Random; import java.util.Set; import java.util.TreeMap; import java.util.function.BiFunction; import java.util.function.DoubleUnaryOperator; import java.util.function.IntBinaryOperator; import java.util.function.IntFunction; import java.util.function.IntToDoubleFunction; import java.util.function.IntToLongFunction; import java.util.function.IntUnaryOperator; import java.util.function.LongBinaryOperator; import java.util.function.LongToDoubleFunction; import java.util.function.Predicate; import java.util.function.ToIntFunction; import java.util.random.RandomGenerator; import java.util.stream.IntStream; import java.util.stream.Stream; public class Main { static MyPrintWriter pw = MyPrintWriter.getInstance(); static FastScanner sc = FastScanner.getInstance(); public static void main(String[] args) throws IOException { Thread.setDefaultUncaughtExceptionHandler((t, e) -> System.exit(1)); new Main().run(); // new Main().test(); pw.flush(); } void run() { int N = sc.nextInt(); int M = sc.nextInt(); int K = sc.nextInt(); Fp fp = Fp.MOD998244353; long mod = fp.modulus(); long[][][] dp = new long[N + 1][N + 1][((N * (N - 1)) / 2) + 1];// 決めた頂点数、最後の頂点数、y=(1+z)の次数 dp[1][1][0] = 1; long[][][] f = new long[N + 1][N + 1][((N * (N - 1)) / 2) + 1]; for (int dist = 1; dist <= (N - 1); dist++) { long[][][] ndp = new long[dp.length][dp[0].length][dp[0][0].length]; for (int k = 1; k < N; k++) { // k頂点使った for (int m = 1; m <= k; m++) { // 最後m頂点 for (int j = 0; j <= ((k * (k - 1)) / 2); j++) { // yの次数 if (dp[k][m][j] == 0) { continue; } for (int l = 0; (k + l) <= N; l++) { // 足す頂点数 int nk = k + l; long way; if (dist < K) { way = fp.comb((N - k) - 1, l); } else if (dist == K) { way = fp.comb((N - k) - 1, l - 1); } else { way = fp.comb(N - k, l); } for (int o = 0; o <= l; ++o) { ndp[nk][l][(j + ((l * (l - 1)) / 2)) + (o * m)] += fp.mul(fp.signedComb(l, l - o), way, dp[k][m][j]); ndp[nk][l][(j + ((l * (l - 1)) / 2)) + (o * m)] %= mod; } } } } } dp = ndp; if (dist >= K) { for (int i = 0; i < f.length; i++) { for (int j = 0; j < f[i].length; j++) { for (int k = 0; k < f[i][j].length; k++) { f[i][j][k] += dp[i][j][k]; f[i][j][k] %= mod; } } } } } long ans = 0; for (int n = 2; n <= N; n++) { // 決めた頂点数、最後の頂点数、y=(1+z)の次数 for (int last = 1; last <= n; last++) { for (int deg = 0; deg <= ((N * (N - 1)) / 2); deg++) { ans += fp.mul(fp.comb(((int) (MathUtils.comb(N - n, 2))) + deg, ((N * (N - 1)) / 2) - M), f[n][last][deg]); ans %= mod; } } } pw.println(ans); } } class ArrayUtils { public static void swap(long[] A, long[] B) { if (A.length != B.length) { throw new AssertionError(); } for (int i = 0; i < A.length; i++) { long tmp = A[i]; A[i] = B[i]; B[i] = tmp; } } } class FastScanner { private static FastScanner instance = null; private final InputStream in = System.in; private final byte[] buffer = new byte[1 << 16]; private int ptr = 0; private int buflen = 0; private FastScanner() { } public static FastScanner getInstance() { if (instance == null) { instance = new FastScanner(); } return instance; } private boolean hasNextByte() { if (ptr < buflen) { return true; } ptr = 0; try { buflen = in.read(buffer); } catch (IOException e) { e.printStackTrace(); } return buflen > 0; } private int readByte() { if (hasNextByte()) { return buffer[ptr++]; } else { return -1; } } private boolean isPrintableChar(int c) { return (33 <= c) && (c <= 126); } public boolean hasNext() { while (hasNextByte() && (!isPrintableChar(buffer[ptr]))) { ptr++; } return hasNextByte(); } public long nextLong() { if (!hasNext()) { throw new NoSuchElementException(); } long n = 0; boolean minus = false; int b = readByte(); if (b == '-') { minus = true; b = readByte(); } while ((b >= '0') && (b <= '9')) { // n = n * 10 + (b - '0'); n = ((n << 1) + (n << 3)) + (b - '0'); b = readByte(); } return minus ? -n : n; } public int nextInt() { return ((int) (nextLong())); } } class Fp extends Zn implements LongFieldStrategy { /** * 998244353 を法とする有限体 F_998244353。 */ public static final Fp MOD998244353 = new Fp(998244353); public Fp(long mod) { super(mod); } /** * fac(n) 用に遅延初期化される階乗テーブル。 */ int[] fac = new int[0]; /** * ifac(n) 用に遅延初期化される逆階乗テーブル。 */ int[] ifac = new int[0]; /** * inv(n) 用に遅延初期化される逆元テーブル。 */ int[] inv = new int[0]; public void expand(int n) { fac = new int[n]; ifac = new int[n]; inv = new int[n]; Arrays.fill(fac, 1); Arrays.fill(ifac, 1); Arrays.fill(inv, 1); for (int i = 2; i < n; ++i) { fac[i] = ((int) ((i * (fac[i - 1] & 0xffffffffL)) % mod)); inv[i] = ((int) (mod - (((mod / i) * (inv[((int) (mod % i))] & 0xffffffffL)) % mod))); ifac[i] = ((int) (((inv[i] & 0xffffffffL) * (ifac[i - 1] & 0xffffffffL)) % mod)); } } public long fac(int n) { if (fac.length <= n) { expand(Math.max(2 * fac.length, n + 1)); } return fac[n] & 0xffffffffL; } public long ifac(int n) { if (ifac.length <= n) { expand(Math.max(2 * ifac.length, n + 1)); } return ifac[n] & 0xffffffffL; } /** * n < 0 でもok * * @param n * @return */ public long inv(long n) { if (n < 0) { n = reduce(n); } return n < inv.length ? inv[((int) (n))] & 0xffffffffL : MathUtils.modInv(n, mod); } /** * 事前条件: b != 0 in F_mod。返り値 r は r = a / b in F_mod。 * 未テスト * 計算量: O(log mod) * * @param a * 被除数 * @param b * 除数 * @return a / b mod mod */ @Override public long div(long a, long b) { return mul(a, inv(b)); } /** * 事前条件: b != 0 in F_mod。返り値 r は r = 0。 * 未テスト * 計算量: O(1) * * @param a * 被除数 * @param b * 除数 * @return 0 */ @Override public long mod(long a, long b) { if (equals(b, zero())) { throw new ArithmeticException("Division by zero"); } return zero(); } /** * 返り値 r は a = 0 なら r = 0、そうでなければ r = 1。 * 未テスト * 計算量: O(1) * * @param a * 対象 * @return a のノルム */ @Override public long norm(long a) { return equals(a, zero()) ? 0 : 1; } /** * 返り値 u は a = 0 なら u = 1、そうでなければ u = a。 * 未テスト * 計算量: O(1) * * @param a * 対象 * @return a = u * canonical(a) を満たす単元 u */ @Override public long canonicalUnit(long a) { if (equals(a, zero())) { return one(); } return a; } /** * comb(0, 0)=1とする。 * * @param n * @param k * @return */ public long comb(int n, int k) { if ((k < 0) || ((n - k) < 0)) { return 0; } return (((fac(n) * ifac(k)) % mod) * ifac(n - k)) % mod; } /** * comb(n, k)(-1)^k * * @param n * @param k * @return */ public long signedComb(int n, int k) { long ret = comb(n, k); if (((k % 2) == 1) && (ret != 0)) { ret = mod - ret; } return ret; } /** * a は 32bit 整数を仮定 */ public long pow(long a, long n) { return MathUtils.modPow(a, n, mod); } } /** * primitive long に特化した可換環の代数的構造。 */ interface LongCommutativeRingStrategy extends LongRingStrategy {} /** * primitive long に特化したユークリッド整域の代数的構造。 */ interface LongEuclideanDomainStrategy extends LongGCDDomainStrategy { /** * * @param a * @param b * @return a / b */ long div(long a, long b); /** * * @param a * @param b * @return a % b */ long mod(long a, long b); /** * * @param a * @return a のノルム */ long norm(long a); /** * a = u * canonical(a) となる単元 u を返す。 * * @param a * @return 単元 u */ default long canonicalUnit(long a) { return one(); } @Override default long gcd(long a, long b) { while (!equals(b, zero())) { a = mod(a, b); long t = a; a = b; b = t; } if (equals(a, zero())) { return a; } return div(a, canonicalUnit(a)); } record ExtGCDResult(long x, long y, long gcd) {} /** * ax + by = gcd(a, b) を解く。 * * @param a * @param b * @return 解 (x, y, gcd) */ default ExtGCDResult extgcd(long a, long b) { long x0 = one(); long y0 = zero(); long g0 = a; long x1 = zero(); long y1 = one(); long g1 = b; while (!equals(g1, zero())) { long q = div(g0, g1); long nextG = sub(g0, mul(q, g1)); long nextX = sub(x0, mul(q, x1)); long nextY = sub(y0, mul(q, y1)); x0 = x1; y0 = y1; g0 = g1; x1 = nextX; y1 = nextY; g1 = nextG; } if (equals(g0, zero())) { return new ExtGCDResult(x0, y0, g0); } long u = canonicalUnit(g0); return new ExtGCDResult(div(x0, u), div(y0, u), div(g0, u)); } } /** * primitive long に特化した除法が可能な環の代数的構造。 */ interface LongExactDivRingStrategy extends LongIntegralDomainStrategy { /** * 割り切れることが保証されている場合に a / b を計算する。 * * @param a * @param b * @return a / b */ long divExact(long a, long b); } /** * primitive long に特化した体の代数的構造。 */ interface LongFieldStrategy extends LongEuclideanDomainStrategy , LongExactDivRingStrategy { @Override default long divExact(long a, long b) { return div(a, b); } /** * * @param a * @return a^-1 */ long inv(long a); /** * * @param a * @param b * @return a / b */ default long div(long a, long b) { return mul(a, inv(b)); } /** * 1 + a + a^2 + ... = 1 / (1 - a) を計算する。 * * @param a * 公比 * @return 等比級数の和 */ default long geometricSum(long a) { return inv(sub(one(), a)); } @Override default LongEuclideanDomainStrategy.ExtGCDResult extgcd(long a, long b) { if (!equals(a, zero())) { return new LongEuclideanDomainStrategy.ExtGCDResult(inv(a), zero(), one()); } else if (!equals(b, zero())) { return new LongEuclideanDomainStrategy.ExtGCDResult(zero(), inv(b), one()); } else { return new LongEuclideanDomainStrategy.ExtGCDResult(zero(), zero(), zero()); } } } /** * primitive long に特化したGCD整域の代数的構造。 */ interface LongGCDDomainStrategy extends LongIntegralDomainStrategy { /** * * @param a * @param b * @return gcd(a, b) */ long gcd(long a, long b); } /** * primitive long に特化した整域の代数的構造。 */ interface LongIntegralDomainStrategy extends LongCommutativeRingStrategy {} /** * primitive long に特化した環の代数的構造。 */ interface LongRingStrategy extends LongSemiRingStrategy { /** * * @param a * @return -a */ long neg(long a); /** * * @param a * @param b * @return a - b */ default long sub(long a, long b) { return add(a, neg(b)); } } /** * primitive long に特化した半環の代数的構造。 */ interface LongSemiRingStrategy { /** * * @return 加法の単位元 */ long zero(); /** * * @return 乗法の単位元 */ long one(); /** * * @param a * @param b * @return a + b */ long add(long a, long b); /** * * @param a * @param b * @return a * b */ long mul(long a, long b); /** * * @param a * @param b * @return a == b */ boolean equals(long a, long b); } class MathUtils { public static long modPow(long a, long n, long mod) { if (n < 0) { long inv = MathUtils.modInv(a, mod); return MathUtils.modPow(inv, -n, mod); } if (n == 0) { return 1; } return (MathUtils.modPow((a * a) % mod, n / 2, mod) * ((n % 2) == 1 ? a : 1)) % mod; } /** * 拡張ユークリッドの互除法で逆元を求める。 * * @param a * @param mod * @return */ public static long modInv(long a, long mod) { a = ((a % mod) + mod) % mod; long[] f0 = new long[]{ 1, 0, mod }; long[] f1 = new long[]{ 0, 1, a }; while (f1[2] != 0) { long q = f0[2] / f1[2]; for (int i = 0; i < 3; i++) { f0[i] -= q * f1[i]; } ArrayUtils.swap(f0, f1); } return f0[1] < 0 ? mod + f0[1] : f0[1]; } /** * O(min(k, n-k)) * C(n,i)=C(n,i-1)(n-i+1)/iで計算 * * @param n * @param k * @return */ public static long comb(int n, int k) { if ((k < 0) || ((n - k) < 0)) { return 0; } if (k > (n / 2)) { return MathUtils.comb(n, n - k); } if (k == 0) { return 1; } if (k == 1) { return n; } if (k == 2) { return ((1L * n) * (n - 1)) / 2; } if (k > (n - k)) { return MathUtils.comb(n, n - k); } long ans = 1; for (int i = 1; i <= k; i++) { // C(n,i)=C(n,i-1)(n-i+1)/i ans = (ans * ((n - i) + 1)) / i; } return ans; } } class MyPrintWriter extends PrintWriter { private static MyPrintWriter instance = null; private MyPrintWriter() { super(System.out); } public static MyPrintWriter getInstance() { if (instance == null) { instance = new MyPrintWriter(); } return instance; } } class Zn implements LongCommutativeRingStrategy { /** * 剰余環 Z/modZ の法。 */ final long mod; public Zn(long mod) { this.mod = mod; } public long modulus() { return this.mod; } /** * 返り値 r は r = 0。 * 未テスト * 計算量: O(1) * * @return 0 */ @Override public long zero() { return 0; } /** * 返り値 r は r ≡ 1 (mod mod) かつ 0 ≤ r < mod。 * 未テスト * 計算量: O(1) * * @return 1 mod mod */ @Override public long one() { return 1 % mod; } public long pow(long a, long n) { if (n < 0) { throw new AssertionError(); } return MathUtils.modPow(a, n, mod); } @Override public long add(long a, long b) { long ret = (a + b) % mod; if (ret < 0) { ret += mod; } return ret; } public long sub(long a, long b) { long ret = (a - b) % mod; if (ret < 0) { ret += mod; } return ret; } @Override public long mul(long a, long b) { return (a * b) % mod; } public long mul(long a, long b, long c) { return (((a * b) % mod) * c) % mod; } /** * 返り値 r は r ≡ -a (mod mod) かつ 0 ≤ r < mod。 * 未テスト * 計算量: O(1) * * @param a * 被減元 * @return -a mod mod */ @Override public long neg(long a) { return a == 0 ? 0 : mod - a; } /** * 返り値 r は r ⇔ a = b。 * 未テスト * 計算量: O(1) * * @param a * 比較対象 * @param b * 比較対象 * @return a == b */ @Override public boolean equals(long a, long b) { return a == b; } /** * * * 剰余を取り、0以上mod未満の値を返す。 * * @param a * @return */ public long reduce(long a) { a %= mod; if (a < 0) { a += mod; } return a; } } // --- Original Code --- // import java.io.IOException; // import java.util.Arrays; // // import library.tools.FastScanner; // import library.tools.MergeFiles; // import library.tools.MyPrintWriter; // import library.util.Fp; // import library.util.MathUtils; // // public class Main { // static MyPrintWriter pw = MyPrintWriter.getInstance(); // static FastScanner sc = FastScanner.getInstance(); // // public static void main(String[] args) throws IOException { // new Main().run(); // // new Main().test(); // pw.flush(); // MergeFiles.export(); // } // // void run() { // int N=sc.nextInt(); // int M=sc.nextInt(); // int K=sc.nextInt(); // Fp fp=Fp.MOD998244353; // long mod=fp.modulus(); // // // long[][][] dp = new long[N + 1][N + 1][((N * (N - 1)) / 2) + 1];// 決めた頂点数、最後の頂点数、y=(1+z)の次数 // // dp[1][1][0] = 1; // long[][][]f=new long[N+1][N+1][N*(N-1)/2+1]; // for (int dist = 1; dist <= N-1; dist++) { // long[][][]ndp=new long[dp.length][dp[0].length][dp[0][0].length]; // for (int k = 1; k < N; k++) { // // k頂点使った // for (int m = 1; m <= k; m++) { // // 最後m頂点 // for (int j = 0; j <= ((k * (k - 1)) / 2); j++) { // // yの次数 // if (dp[k][m][j] == 0) { // continue; // } // for (int l = 0; (k + l) <= N; l++) { // // 足す頂点数 // int nk = k + l; // long way; // if (dist < K) { // way = fp.comb(N - k - 1, l); // } else if (dist == K) { // way = fp.comb(N - k - 1, l - 1); // } else { // way = fp.comb(N - k, l); // } // for (int o = 0; o <= l; ++o) { // ndp[nk][l][(j + ((l * (l - 1)) / 2)) + (o * m)] += fp.mul(fp.signedComb(l, l - o), way, dp[k][m][j]); // ndp[nk][l][(j + ((l * (l - 1)) / 2)) + (o * m)] %= mod; // } // } // } // } // } // dp=ndp; // if (dist >= K) { // for (int i = 0; i < f.length; i++) { // for (int j = 0; j < f[i].length; j++) { // for (int k = 0; k < f[i][j].length; k++) { // f[i][j][k]+=dp[i][j][k]; // f[i][j][k]%=mod; // } // } // } // } // } // long ans=0; // for (int n = 2; n <= N; n++) { // // 決めた頂点数、最後の頂点数、y=(1+z)の次数 // for (int last = 1; last <= n; last++) { // for (int deg = 0; deg <= ((N * (N - 1)) / 2); deg++) { // ans += fp.mul(fp.comb((int)MathUtils.comb(N-n, 2) + deg, N*(N-1)/2-M), f[n][last][deg]); // ans %= mod; // } // } // } // pw.println(ans); // } // // // void tr(Object... objects) { // System.out.println(Arrays.deepToString(objects)); // } // } //