#include using namespace std; using ll = long long; #define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i) #define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i) #define all(x) begin(x), end(x) template bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; } template bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; } template class Modint { using mint = Modint; static_assert(mod > 0, "Modulus must be positive"); public: static constexpr int get_mod() noexcept { return mod; } constexpr Modint(long long y = 0) noexcept : x(y >= 0 ? y % mod : (y % mod + mod) % mod) {} constexpr int value() const noexcept { return x; } constexpr mint& operator+=(const mint& r) noexcept { if ((x += r.x) >= mod) x -= mod; return *this; } constexpr mint& operator-=(const mint& r) noexcept { if ((x += mod - r.x) >= mod) x -= mod; return *this; } constexpr mint& operator*=(const mint& r) noexcept { x = static_cast(1LL * x * r.x % mod); return *this; } constexpr mint& operator/=(const mint& r) noexcept { *this *= r.inv(); return *this; } constexpr mint operator-() const noexcept { return mint(-x); } constexpr mint operator+(const mint& r) const noexcept { return mint(*this) += r; } constexpr mint operator-(const mint& r) const noexcept { return mint(*this) -= r; } constexpr mint operator*(const mint& r) const noexcept { return mint(*this) *= r; } constexpr mint operator/(const mint& r) const noexcept { return mint(*this) /= r; } constexpr bool operator==(const mint& r) const noexcept { return x == r.x; } constexpr bool operator!=(const mint& r) const noexcept { return x != r.x; } constexpr mint inv() const noexcept { int a = x, b = mod, u = 1, v = 0; while (b > 0) { int t = a / b; std::swap(a -= t * b, b); std::swap(u -= t * v, v); } return mint(u); } constexpr mint pow(long long n) const noexcept { mint ret(1), mul(x); while (n > 0) { if (n & 1) ret *= mul; mul *= mul; n >>= 1; } return ret; } friend std::ostream& operator<<(std::ostream& os, const mint& r) { return os << r.x; } friend std::istream& operator>>(std::istream& is, mint& r) { long long t; is >> t; r = mint(t); return is; } private: int x; }; using mint = Modint<998244353>; template class Matrix { public: static Matrix concat(const Matrix& A, const Matrix& B) { assert(A.m == B.m); Matrix C(A.m, A.n + B.n); for (int i = 0; i < A.m; ++i) { std::copy(A[i].begin(), A[i].end(), C[i].begin()); std::copy(B[i].begin(), B[i].end(), C[i].begin() + A.n); } return C; } Matrix() = default; Matrix(int m, int n) : mat(m, std::vector(n)), m(m), n(n) {} Matrix(std::initializer_list> list) { for (auto& l : list) mat.emplace_back(l); m = mat.size(); n = mat[0].size(); } int row() const { return m; } int col() const { return n; } const std::vector& operator[](int i) const { return mat[i]; } std::vector& operator[](int i) { return mat[i]; } Matrix& operator+=(const Matrix& rhs) { assert(m == rhs.m && n == rhs.n); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { mat[i][j] += rhs[i][j]; } } return *this; } Matrix& operator-=(const Matrix& rhs) { assert(m == rhs.m && n == rhs.n); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { mat[i][j] -= rhs[i][j]; } } return *this; } Matrix operator+(const Matrix& rhs) const { return Matrix(*this) += rhs; } Matrix operator-(const Matrix& rhs) const { return Matrix(*this) -= rhs; } Matrix transpose() const { Matrix ret(n, m); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { ret[i][j] = mat[j][i]; } } return ret; } Matrix matmul(const Matrix& B) const { assert(n == B.m); Matrix ret(m, B.n); for (int i = 0; i < m; ++i) { for (int j = 0; j < B.n; ++j) { for (int k = 0; k < n; ++k) { ret[i][j] += mat[i][k] * B[k][j]; } } } return ret; } Matrix rref() const { Matrix A(*this); int pivot = 0; for (int j = 0; j < n; ++j) { int i = pivot; while (i < m && eq(A[i][j], T(0))) ++i; if (i == m) continue; if (i != pivot) A[i].swap(A[pivot]); T p = A[pivot][j]; for (int l = j; l < n; ++l) A[pivot][l] /= p; for (int k = 0; k < m; ++k) { if (k == pivot) continue; T v = A[k][j]; for (int l = j; l < n; ++l) { A[k][l] -= A[pivot][l] * v; } } ++pivot; } return A; } int rank() const { auto A = rref(); for (int i = 0; i < m; ++i) { bool nonzero = false; for (int j = 0; j < n; ++j) { if (!eq(A[i][j], T(0))) { nonzero = true; break; } } if (!nonzero) return i; } return m; } template ::value>::type* = nullptr> static constexpr bool eq(U a, U b) { return std::abs(a - b) < 1e-8; } template ::value>::type* = nullptr> static constexpr bool eq(U a, U b) { return a == b; } protected: std::vector> mat; int m, n; }; template class SquareMatrix : public Matrix { using Matrix::Matrix; using Matrix::eq; using Matrix::n; public: static SquareMatrix I(int n) { SquareMatrix ret(n); for (int i = 0; i < n; ++i) ret[i][i] = 1; return ret; } SquareMatrix() = default; explicit SquareMatrix(int n) : Matrix(n, n) {} SquareMatrix(const Matrix& mat) : Matrix(mat) { assert(Matrix::m == n); } SquareMatrix(std::initializer_list> list) : Matrix(list) { assert(Matrix::m == n); } SquareMatrix pow(long long k) const { auto ret = I(n); auto A(*this); while (k > 0) { if (k & 1) ret = ret.matmul(A); A = A.matmul(A); k >>= 1; } return ret; } T det() const { SquareMatrix A(*this); T ret = 1; for (int j = 0; j < n; ++j) { int i = j; while (i < n && eq(A[i][j], T(0))) ++i; if (i == n) return 0; if (i != j) { A[i].swap(A[j]); ret = -ret; } T p = A[j][j]; ret *= p; for (int l = j; l < n; ++l) A[j][l] /= p; for (int k = j + 1; k < n; ++k) { T v = A[k][j]; for (int l = j; l < n; ++l) { A[k][l] -= A[j][l] * v; } } } return ret; } SquareMatrix inv() const { assert(!eq(det(), T(0))); auto IB = Matrix::concat(*this, I(n)).rref(); SquareMatrix B(n); for (int i = 0; i < n; ++i) { std::copy(IB[i].begin() + n, IB[i].end(), B[i].begin()); } return B; } }; using Mat = SquareMatrix; int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); cout << fixed << setprecision(15); int N, K; cin >> N >> K; vector>> edges(K); rep(i, 0, K) { int t; cin >> t; rep(_, 0, t) { int a, b; cin >> a >> b; --a, --b; edges[i].push_back({a, b}); } } auto calc = [&](vector>& edges) { Mat mat(N - 1); for (auto [a, b] : edges) { if (a > b) swap(a, b); if (a != N - 1) mat[a][a] += 1; if (b != N - 1) mat[b][b] += 1; if (a != N - 1 && b != N - 1) { mat[a][b] -= 1; mat[b][a] -= 1; } } return mat.det(); }; mint ans = 0; rep(S, 1, 1 << K) { vector> use; rep(i, 0, K) { if (S >> i & 1) { for (auto [a, b] : edges[i]) use.push_back({a, b}); } } ans += __builtin_popcount(S) % 2 == K % 2 ? calc(use) : -calc(use); } cout << ans << endl; }