#include using namespace std; using ll = long long; static const int MOD = 998244353; ll modpow(ll a, ll e) { ll r = 1; while (e > 0) { if (e & 1) r = r * a % MOD; a = a * a % MOD; e >>= 1; } return r; } struct Comb { vector fact, ifact; void init(int n) { fact.assign(n + 1, 1); ifact.assign(n + 1, 1); for (int i = 1; i <= n; i++) fact[i] = (ll)fact[i - 1] * i % MOD; ifact[n] = (int)modpow(fact[n], MOD - 2); for (int i = n; i >= 1; i--) ifact[i - 1] = (ll)ifact[i] * i % MOD; } int C(int n, int r) const { if (r < 0 || r > n) return 0; return (ll)fact[n] * ifact[r] % MOD * ifact[n - r] % MOD; } }; vector mul_qs_minus_1_full(const vector &p, int s, int E) { vector res(E + 1, 0); if (s <= E) { for (int i = 0; i + s <= E; i++) { res[i + s] = p[i]; } } for (int i = 0; i <= E; i++) { res[i] -= p[i]; if (res[i] < 0) res[i] += MOD; } return res; } void add_shift_scaled_full(vector &dst, const vector &src, int shift, int scale, int E) { if (scale == 0 || shift > E) return; for (int i = 0; i + shift <= E; i++) { if (src[i] == 0) continue; dst[i + shift] = (dst[i + shift] + (ll)src[i] * scale) % MOD; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, M, K; cin >> N >> M >> K; int E = N * (N - 1) / 2; int R = E - M; Comb comb; comb.init(E); // 嘘ポイント: // 頂点 2,3,...,N を全部「普通の頂点」として扱ってしまう。 // 本来は 2,3,...,N-1 の N-2 個だけを普通頂点にすべき。 int S = N - 1; vector>> dp( S + 1, vector>(S + 2, vector(E + 1, 0)) ); vector> active(S + 1, vector(S + 2, 0)); dp[S][1][0] = 1; active[S][1] = 1; // 第 K 層まで作る // この中に N が含まれているはず、という雑な考え for (int step = 0; step < K; step++) { vector>> ndp( S + 1, vector>(S + 2, vector(E + 1, 0)) ); vector> nactive(S + 1, vector(S + 2, 0)); for (int r = 0; r <= S; r++) { for (int s = 0; s <= S + 1; s++) { if (!active[r][s]) continue; vector cur = dp[r][s]; for (int t = 0; t <= r; t++) { int shift = t * (t - 1) / 2; int ways = comb.C(r, t); add_shift_scaled_full(ndp[r - t][t], cur, shift, ways, E); nactive[r - t][t] = 1; if (t != r) { cur = mul_qs_minus_1_full(cur, s, E); } } } } dp.swap(ndp); active.swap(nactive); } // H(q): 第 K 層まで作った後、最後の層に N がいるとみなす vector H(E + 1, 0); for (int r = 0; r <= S; r++) { for (int s = 0; s <= S + 1; s++) { if (!active[r][s]) continue; if (s == 0) continue; const vector &p = dp[r][s]; // 嘘ポイント: // 第K層の s 個のうち 1 個が N である確率/割合のような補正を入れる。 // ラベル付き数え上げとしては一般に正しくない。 // // 全 S=N-1 個の特別でない扱いの頂点のうち、 // 最後の層に入った s 個から N を選ぶ、というつもり。 // // 実際には、途中の層に N が入っていないことを保証していないので破綻する。 int ways_N_in_last = s; // 残り r 個の頂点は第K層より後ろ、または到達不能として自由に扱うつもり。 // ここでも本来の構造とはズレる。 int base = s * r + r * (r - 1) / 2; for (int i = 0; i <= E; i++) { if (p[i] == 0) continue; if (i + base <= E) { H[i + base] = (H[i + base] + (ll)p[i] * ways_N_in_last) % MOD; } } } } ll ans = 0; for (int c = R; c <= E; c++) { ans += (ll)H[c] * comb.C(c, R) % MOD; ans %= MOD; } cout << ans << '\n'; return 0; }