#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 7, mod = 998244353;
int pow_mod(int x, int n) {
  int res = 1;
  while (n & 1) {
    if (n & 1) res = 1LL * res * x % mod;
    x = 1LL * x * x % mod;
    n >>= 1;
  }
  return res;
}
struct PQtree {
  int siz[N], sizr[N], typ[N], n, nodecnt, fail, *s;
  vector<int> con[N];
  inline int state(int x) {
    if (!sizr[x]) return 0;
    if (sizr[x] == siz[x]) return 2;
    return 1;
  }
  void init(int N) {
    n = N;
    nodecnt = n + 1;
    for (int i = 1; i <= n; i++) con[n + 1].push_back(i);
  }
  void dfs(int np) {
    siz[np] = (np <= n);
    sizr[np] = (np <= n) ? (s[np] == 1) : 0;
    for (int &x : con[np]) dfs(x), siz[np] += siz[x], sizr[np] += sizr[x];
  }
  int mergep(const vector<int> &v) {
    if (!v.size()) return 0;
    if (v.size() == 1) return v[0];
    int p = ++nodecnt;
    con[p] = v;
    return p;
  }
  void addedge(int s, int e) {
    if (e) con[s].push_back(e);
  }
  void merge(int a, int b) {
    con[a].insert(con[a].end(), con[b].begin(), con[b].end());
  }
  int solve(int np, int t) {
    if (fail) return 0;
    vector<int> v[3];
    for (int &x : con[np]) v[state(x)].push_back(x);
    if (v[1].size() > 2 || (t > 0 && v[1].size() > 1)) {
      fail = 1;
      return 0;
    }
    if (t == 0 && v[1].size() == 1 && v[2].size() == 0)
      return solve(v[1][0], 0);
    if (typ[np] == 0) {
      int P = mergep(v[2]);
      if (!t) {
        con[np] = v[0];
        if (!v[1].size())
          addedge(np, P);
        else {
          int p1 = solve(v[1][0], 1);
          addedge(p1, P);
          if (v[1].size() > 1) merge(p1, solve(v[1][1], 2));
          addedge(np, p1);
        }
      } else {
        typ[np] = 1;
        con[np].clear();
        addedge(np, mergep(v[0]));
        if (v[1].size()) merge(np, solve(v[1][0], 1));
        addedge(np, P);
        if (t == 2) reverse(con[np].begin(), con[np].end());
      }
      return np;
    } else {
      if (state(con[np][0]) == 2 || state(con[np].back()) == 0)
        reverse(con[np].begin(), con[np].end());
      vector<int> tmp;
      int cnt = 0;
      for (int &x : con[np]) {
        int S = state(x);
        if (S == 1) {
          int p;
          if (cnt == 0)
            cnt++, p = solve(x, 1);
          else if (cnt == 1)
            cnt++, p = solve(x, 2);
          else {
            fail = 1;
            return 0;
          }
          for (int &x : con[p]) tmp.push_back(x);
        } else if (S == 2) {
          tmp.push_back(x);
          if (cnt == 0)
            cnt++;
          else if (cnt == 2) {
            fail = 1;
            return 0;
          }
        } else {
          tmp.push_back(x);
          if (cnt == 1) cnt++;
        }
      }
      if (t && cnt == 2) {
        fail = 1;
        return 0;
      }
      con[np] = tmp;
      if (t == 2) reverse(con[np].begin(), con[np].end());
      return np;
    }
  }
  void regulate(int *S) {
    s = S;
    dfs(n + 1);
    solve(n + 1, 0);
  }
  int cal(int x) {
    if (x <= n) return 1;
    int res = 1, d = 0;
    for (auto &y : con[x]) res = 1LL * res * cal(y) % mod, d++;
    if (!typ[x])
      for (int i = 2; i <= d; ++i) res = 1LL * res * i % mod;
    else if (d > 1)
      res = (res << 1) % mod;
    return res;
  }
  int count() { return fail ? 0 : cal(n + 1); }
} T;
int n, m, i, j, k, T_T, s[N];
int main() {
  for (scanf("%d%d", &n, &m), T.init(n); m--;) {
    scanf("%d", &i);
    memset(s, 0, sizeof(s));
    for (j = 1; j <= i; ++j) scanf("%d", &k), s[k] = 1;
    T.regulate(s);
  }
  printf("%d\n", T.count());
}
