// n <= 7 まで愚直解と突き合わせる #include #include #include #include #include using namespace std; #include "testlib.h" #include #include using mint = atcoder::modint998244353; template struct acl_fac { std::vector facs, facinvs; acl_fac(int N) { assert(-1 <= N and N < modint::mod()); facs.resize(N + 1, 1); for (int i = 1; i <= N; i++) facs[i] = facs[i - 1] * i; facinvs.assign(N + 1, facs.back().inv()); for (int i = N; i > 0; i--) facinvs[i - 1] = facinvs[i] * i; } modint operator[](int i) const { return facs.at(i); } modint finv(int i) const { return facinvs.at(i); } }; constexpr int max_n = 2000; acl_fac fac(max_n); #include #include #include #include // UnionFind, able to rewind to the previous state by undo() // Written for Educational Codeforces 62 F, although not verified yet. struct UndoUnionFind { using pint = std::pair; std::vector par, cou; std::stack> history; UndoUnionFind(int N) : par(N), cou(N, 1) { std::iota(par.begin(), par.end(), 0); } int find(int x) const { return (par[x] == x) ? x : find(par[x]); } bool unite(int x, int y) { x = find(x), y = find(y); if (cou[x] < cou[y]) std::swap(x, y); history.emplace(y, pint(par[y], cou[x])); return x != y ? par[y] = x, cou[x] += cou[y], true : false; } void undo() { cou[par[history.top().first]] = history.top().second.second; par[history.top().first] = history.top().second.first; history.pop(); } void reset() { while (!history.empty()) undo(); } int count(int x) const { return cou[find(x)]; } bool same(int x, int y) const { return find(x) == find(y); } }; vector> gen_bruteforce_table(int maxn) { vector> ret(1); for (int n = 1; n <= maxn; ++n) { vector> edges; for (int i = 0; i < n; ++i) { for (int j = i; j < n; ++j) edges.emplace_back(i, j); } vector tree_masks; UndoUnionFind uf(n); auto rec = [&](auto &&self, int d, int m) -> void { if (uf.count(0) == n) { tree_masks.push_back(m); return; } if (d == int(edges.size())) return; self(self, d + 1, m); auto [s, t] = edges.at(d); if (!uf.same(s, t)) { uf.unite(s, t); self(self, d + 1, m + (1 << d)); uf.undo(); } }; rec(rec, 0, 0); vector tmp(n); for (auto m1 : tree_masks) { for (auto m2 : tree_masks) { int k = __builtin_popcount(m1 & m2); tmp.at(k) += 1; } } ret.push_back(tmp); } return ret; } int main(int argc, char *argv[]) { registerValidation(argc, argv); vector f(max_n + 1); for (int n = 1; n <= max_n; ++n) { f.at(n) = (n == 1 ? 1 : mint(n).pow(n - 2)) * n * n * fac.finv(n); } vector> dp(max_n + 1); dp.at(0) = vector(max_n + 1); dp.at(0).at(0) = 1; for (int k = 1; k <= max_n; ++k) { dp.at(k) = atcoder::convolution(f, dp.at(k - 1)); dp.at(k).resize(max_n + 1); } for (int k = 1; k <= max_n; ++k) { for (int n = k; n <= max_n; ++n) { if (k > 1) { dp.at(k).at(n) *= fac[n] * fac.finv(k) * mint(n).pow(2 * (k - 2)); } else { dp.at(k).at(n) = (n > 1 ? mint(n).pow(n - 2) : 1); } } } vector> answers(1); for (int n = 1; n <= max_n; ++n) { vector g(n); for (int i = 0; i < n; ++i) g.at(i) = dp.at(n - i).at(n) * fac[i] * (i % 2 ? -1 : 1); vector fac_trans(n); for (int i = 0; i < n; ++i) fac_trans.at(i) = fac.finv(i); reverse(g.begin(), g.end()); g = atcoder::convolution(g, fac_trans); g.resize(n); reverse(g.begin(), g.end()); for (int i = 0; i < n; ++i) g.at(i) *= fac.finv(i) * (i % 2 ? -1 : 1); answers.push_back(g); } vector> bf_answers = gen_bruteforce_table(7); for (int n = 2; n <= 7; ++n) { for (int k = 0; k <= n - 1; ++k) assert(answers.at(n).at(k) == bf_answers.at(n).at(k)); } int T = inf.readInt(1, 500000); inf.readEoln(); while (T--) { const int n = inf.readInt(2, max_n); inf.readSpace(); const int k = inf.readInt(0, n - 1); inf.readEoln(); cout << answers.at(n).at(k).val() << '\n'; } inf.readEof(); }