#include #include #include // #include // https://github.com/amiast/cpp-utility #ifndef KOTONE_MATRIX_HPP #define KOTONE_MATRIX_HPP 1 #include #include // #include // https://github.com/amiast/cpp-utility #ifndef KOTONE_INTERNAL_TYPE_TRAITS_HPP #define KOTONE_INTERNAL_TYPE_TRAITS_HPP 1 #include namespace kotone { template concept number = std::is_arithmetic_v; template concept signed_number = std::signed_integral || std::floating_point; template concept compatible_modint = requires(mint a, mint b, int64_t x) { { mint::mod() } -> std::convertible_to; mint{}; mint{1}; mint{x}; { a + b } -> std::same_as; { a - b } -> std::same_as; { -a } -> std::same_as; { a * b } -> std::same_as; { a / b } -> std::same_as; { a.inv() } -> std::same_as; { a.pow(x) } -> std::same_as; { a += b } -> std::same_as; { a -= b } -> std::same_as; { a *= b } -> std::same_as; { a /= b } -> std::same_as; { a == b } -> std::convertible_to; { a != b } -> std::convertible_to; { a.val() } -> std::convertible_to; }; template concept additive = requires(T a, T b) { T{}; { a + b } -> std::same_as; { a - b } -> std::same_as; { -a } -> std::same_as; { a == b } -> std::convertible_to; { a != b } -> std::convertible_to; }; template concept mutable_additive = ( additive && requires(T a, T b) { { a += b } -> std::same_as; { a -= b } -> std::same_as; { ++a } -> std::same_as; { --a } -> std::same_as; { a++ } -> std::same_as; { a-- } -> std::same_as; } ); } // namespace kotone #endif // KOTONE_INTERNAL_TYPE_TRAITS namespace kotone { namespace matrix { template using matrix = std::vector>; // Returns the identity matrix of the specified dimension. template matrix identity(int dimension) { assert(dimension >= 0); std::vector result(dimension, std::vector(dimension)); for (int i = 0; i < dimension; i++) result[i][i] = 1; return result; } // Returns the product of two matrices. // Requires `a` and `b` to be valid matrices. // Requires the product to exist. template matrix prod(const matrix &a, const matrix &b) { if (a.empty() && b.empty()) return {}; assert(!a.empty() && !b.empty() && a[0].size() == b.size()); int n = a.size(), m = b.size(), l = b[0].size(); std::vector result(n, std::vector(l)); for (int i = 0; i < n; i++) { for (int k = 0; k < m; k++) { for (int j = 0; j < l; j++) { result[i][j] += a[i][k] * b[k][j]; } } } return result; } // Returns the matrix raised to the specified exponent `k`. // Requires `mat` to be a square matrix. template matrix pow(const matrix &mat, int64_t k) { // assert(k >= 0); if (k == 0) return identity(mat.size()); if (k == 1) return mat; matrix result = pow(mat, k / 2); result = prod(result, result); if (k % 2 == 1) result = prod(result, mat); return result; } // Returns a basis of the specified matrix. // Requires `mat` to be a valid matrix. template matrix basis(matrix mat) { if (mat.empty()) return {}; int n = mat.size(), m = mat[0].size(); int r = 0; for (int k = 0, j = 0; k < n && j < m; k++, j++, r++) { while (j < m) { for (int i = k; i < n; i++) { if (mat[i][k] == 0) continue; if (i != k) std::swap(mat[i], mat[k]); break; } if (mat[k][j] != 0) break; j++; } if (j == m) break; mint inv = mat[k][j].inv(); for (int nj = 0; nj < m; nj++) mat[k][nj] *= inv; for (int i = k + 1; i < n; i++) { for (int nj = m - 1; nj >= j; nj--) mat[i][nj] -= mat[i][j] * mat[k][nj]; } } mat.resize(r); return mat; } // Returns the determinant specified matrix. // Requires `mat` to be a square matrix. template mint det(matrix mat) { int n = mat.size(); mint result = 1; for (int k = 0; k < n; k++) { for (int i = k; i < n; i++) { if (mat[i][k] == 0) continue; if (i != k) { std::swap(mat[i], mat[k]); result = -result; } break; } if (mat[k][k] == 0) return 0; result *= mat[k][k]; mint inv = mat[k][k].inv(); for (int j = 0; j < n; j++) mat[k][j] *= inv; for (int i = k + 1; i < n; i++) { for (int j = n - 1; j >= k; j--) mat[i][j] -= mat[i][k] * mat[k][j]; } } return result; } // Returns a pair `{inv, found}` representing the inverse of the matrix such that // - `inv` is the inverse matrix and `found == true` if the inverse exists, or // - `inv` is empty and `found == false` if the inverse does not exist. // // Requires `mat` to be a square matrix. template std::pair, bool> inv(matrix mat) { int n = mat.size(); for (int i = 0; i < n; i++) { mat[i].resize(n * 2); mat[i][i + n] = 1; } for (int k = 0; k < n; k++) { for (int i = k; i < n; i++) { if (mat[i][k] == 0) continue; if (i != k) std::swap(mat[i], mat[k]); break; } if (mat[k][k] == 0) return {{}, false}; mint inv = mat[k][k].inv(); for (int j = 0; j < n * 2; j++) mat[k][j] *= inv; for (int i = 0; i < n; i++) { if (i == k) continue; for (int j = n * 2 - 1; j >= k; j--) mat[i][j] -= mat[i][k] * mat[k][j]; } } for (int i = 0; i < n; i++) mat[i].erase(mat[i].begin(), mat[i].begin() + n); return {mat, true}; } // Returns the upper Hessenberg form of the specified matrix. // Requires `mat` to be a square matrix. // Reference: https://judge.yosupo.jp/problem/characteristic_polynomial template matrix upper_hessenberg(matrix mat) { int n = mat.size(); for (int k = 0; k < n - 2; k++) { for (int i = k + 1; i < n; i++) { if (mat[i][k] == 0) continue; if (i != k + 1) { std::swap(mat[i], mat[k + 1]); for (int ni = 0; ni < n; ni++) std::swap(mat[ni][i], mat[ni][k + 1]); } break; } if (mat[k + 1][k] == 0) continue; mint inv = mat[k + 1][k].inv(); for (int i = k + 2; i < n; i++) { mint acc = mat[i][k] * inv; for (int j = k; j < n; j++) mat[i][j] -= acc * mat[k + 1][j]; for (int j = 0; j < n; j++) mat[j][k + 1] += acc * mat[j][i]; } } return mat; } // Returns the characteristic polynomial of the specified matrix. // Requires `mat` to be a square matrix. // Reference: https://judge.yosupo.jp/problem/characteristic_polynomial template std::vector charpoly(const matrix &mat) { if (mat.empty()) return {1}; int n = mat.size(); matrix hess = upper_hessenberg(mat); matrix poly(n + 1); poly[0] = {1}; poly[1] = {-hess[0][0], 1}; for (int i = 2; i <= n; i++) { poly[i].resize(i + 1); for (int j = 0; j < i; j++) poly[i][j] = poly[i - 1][j] * -hess[i - 1][i - 1]; for (int j = 0; j < i; j++) poly[i][j + 1] += poly[i - 1][j]; mint acc = 1; for (int j = 1; j < i; j++) { acc *= hess[i - j][i - j - 1]; mint prod = acc * hess[i - j - 1][i - 1]; for (int k = 0; k < i - j; k++) poly[i][k] -= prod * poly[i - j - 1][k]; } } return poly[n]; } } // namespace matrix } // namespace kotone #endif // KOTONE_MATRIX_HPP using mint = atcoder::modint998244353; std::vector det(std::vector> m0, std::vector> m1) { int n = m0.size(), d = 0; mint det = 1; for (int k = 0; k < n; k++) { while (m1[k][k] == 0) { for (int j = k + 1; j < n; j++) { if (m1[k][j] == 0) continue; for (int i = 0; i < n; i++) std::swap(m0[i][k], m0[i][j]); for (int i = 0; i < n; i++) std::swap(m1[i][k], m1[i][j]); det = -det; break; } if (m1[k][k] != 0) break; if (++d > n) return std::vector(n + 1); std::swap(m0[k], m1[k]); for (int i = 0; i < k; i++) { mint acc = m1[k][i]; for (int j = 0; j < n; j++) m0[k][j] -= acc * m0[i][j]; for (int j = 0; j < n; j++) m1[k][j] -= acc * m1[i][j]; } } det *= m1[k][k]; mint inv = m1[k][k].inv(); for (mint &m : m0[k]) m *= inv; for (mint &m : m1[k]) m *= inv; for (int i = 0; i < n; i++) { if (i == k) continue; mint acc = m1[i][k]; for (int j = 0; j < n; j++) m0[i][j] -= acc * m0[k][j]; for (int j = 0; j < n; j++) m1[i][j] -= acc * m1[k][j]; } } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) m0[i][j] = -m0[i][j]; } std::vector p = kotone::matrix::charpoly(m0); std::vector result(n + 1); for (int i = 0; i + d <= n; i++) result[i] = det * p[i + d]; return result; } int main() { int N; std::cin >> N; std::vector M(2, std::vector(N, std::vector(N))); for (int k = 0; k < 2; k++) { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int a; std::cin >> a; M[k][i][j] = a; } } } for (mint &m : det(M[0], M[1])) std::cout << m.val() << std::endl; }