結果
| 問題 | No.3589 Make Ends Meet (Hard) |
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2026-07-10 10:03:59 |
| 言語 | PyPy3 (7.3.17) |
| 結果 |
AC
|
| 実行時間 | 134 ms / 2,000 ms |
| コード長 | 3,601 bytes |
| 記録 | |
| コンパイル時間 | 226 ms |
| コンパイル使用メモリ | 95,980 KB |
| 実行使用メモリ | 87,168 KB |
| 最終ジャッジ日時 | 2026-07-10 21:03:07 |
| 合計ジャッジ時間 | 5,498 ms |
|
ジャッジサーバーID (参考情報) |
judge3_1 / judge1_0 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 47 |
ソースコード
import sys
MOD = 998244353
def solve() -> None:
N, M, K = map(int, sys.stdin.readline().split())
total_edges = N * (N - 1) // 2
remaining_edges = total_edges - M
limit = max(N, total_edges)
fact = [1] * (limit + 1)
for i in range(1, limit + 1):
fact[i] = fact[i - 1] * i % MOD
inv_fact = [1] * (limit + 1)
inv_fact[limit] = pow(fact[limit], MOD - 2, MOD)
for i in range(limit, 0, -1):
inv_fact[i - 1] = inv_fact[i] * i % MOD
def comb(n: int, r: int) -> int:
if r < 0 or r > n:
return 0
return fact[n] * inv_fact[r] % MOD * inv_fact[n - r] % MOD
small_comb = [[0] * (N + 1) for _ in range(N + 1)]
for n in range(N + 1):
for r in range(n + 1):
small_comb[n][r] = comb(n, r)
choose_remaining_edges = [
comb(q, remaining_edges)
for q in range(total_edges + 1)
]
# dp[(used, last)][q]:
# used 頂点を使い、最後の距離層のサイズが last である構成の
# (1+x)^q 基底における係数
dp: dict[tuple[int, int], list[int]] = {
(1, 1): [1]
}
# L_1, ..., L_{K-1} を構成
for layer in range(1, K):
next_dp: dict[tuple[int, int], list[int]] = {}
future_layers = K - 1 - layer
for (used, previous_size), polynomial in dp.items():
# 頂点 N は最後の集合 R に残す
selectable = N - 1 - used
# 残りの非空層に最低1頂点ずつ残す
max_new_size = selectable - future_layers
for new_size in range(1, max_new_size + 1):
new_used = used + new_size
key = (new_used, new_size)
destination = next_dp.get(key)
if destination is None:
max_degree = new_used * (new_used - 1) // 2
destination = [0] * (max_degree + 1)
next_dp[key] = destination
choose_vertices = small_comb[selectable][new_size]
internal_edges = new_size * (new_size - 1) // 2
for j in range(new_size + 1):
coefficient = (
choose_vertices
* small_comb[new_size][j]
% MOD
)
if (new_size - j) & 1:
coefficient = MOD - coefficient
shift = internal_edges + previous_size * j
for q, value in enumerate(polynomial):
if value == 0:
continue
index = q + shift
destination[index] = (
destination[index]
+ coefficient * value
) % MOD
dp = next_dp
answer = 0
for (used, last_size), polynomial in dp.items():
rest_size = N - used
# R内部、および R\{N} と最後の層の間の自由な辺
free_edges = (
rest_size * (rest_size - 1) // 2
+ last_size * (rest_size - 1)
)
for q, value in enumerate(polynomial):
if value == 0:
continue
# 頂点Nと最後の層の間には1本以上の辺が必要
ways = (
choose_remaining_edges[q + free_edges + last_size]
- choose_remaining_edges[q + free_edges]
) % MOD
answer = (answer + value * ways) % MOD
print(answer)
if __name__ == "__main__":
solve()