#include <bits/stdc++.h>
using namespace std;
long long fac[128];
long long fp(long long a, long long n, long long mod = 998244353) {
  if (n < 0) a = fp(a, mod - 2, mod), n = -n;
  long long res = 1;
  for (; n; n >>= 1, a = a * a % mod)
    if (n & 1) res = res * a % mod;
  return res;
}
const int maxn = (int)2e5 + 20;
const int maxm = (int)1e6 + 20;
const int N = 500 + 5;
struct PQ_tree {
  int n, tot;
  int fail;
  vector<int> G[N << 2];
  int ty[N << 2];
  int sz[N << 2];
  int szc[N << 2];
  bool s[N];
  inline int getstate(int u) {
    if (szc[u] == 0) return 0;
    if (szc[u] == sz[u]) return 2;
    return 1;
  }
  void addson(int x, int y) {
    if (y) G[x].push_back(y);
  }
  void join(int x, int y) {
    for (auto v : G[y]) G[x].push_back(v);
  }
  int mergeP(vector<int> &vec) {
    if (vec.size() == 0) return 0;
    if (vec.size() == 1) return vec[0];
    G[++tot] = vec;
    return tot;
  }
  void init(int _n) {
    n = _n;
    tot = n + 1;
    memset(ty, 0, sizeof(ty));
    for (int i = 1; i <= n; i++) G[n + 1].push_back(i);
    fail = 0;
  }
  void dfs(int u) {
    sz[u] = u <= n;
    szc[u] = u <= n && s[u];
    for (auto v : G[u]) {
      dfs(v);
      sz[u] += sz[v];
      szc[u] += szc[v];
    }
  }
  int check(int u, int t) {
    if (fail) return 0;
    vector<int> vec[3];
    for (auto v : G[u]) vec[getstate(v)].push_back(v);
    if (vec[1].size() > 2 || (t && vec[1].size() > 1)) return fail = 1, 0;
    if (t == 0 && vec[1].size() == 1 && vec[2].size() == 0)
      return check(vec[1][0], 0);
    if (ty[u] == 0) {
      int p2 = mergeP(vec[2]);
      if (t == 0) {
        G[u] = vec[0];
        if (vec[1].size() == 0)
          addson(u, p2);
        else {
          int tmp1 = check(vec[1][0], 2);
          addson(tmp1, p2);
          if (vec[1].size() == 2) join(tmp1, check(vec[1][1], 1));
          addson(u, tmp1);
        }
        return u;
      } else {
        ty[u] = 1;
        G[u].clear();
        addson(u, p2);
        if (vec[1].size() == 1) join(u, check(vec[1][0], 1));
        addson(u, mergeP(vec[0]));
        if (t == 2) reverse(G[u].begin(), G[u].end());
      }
      return u;
    } else {
      if (getstate(G[u].front()) > getstate(G[u].back()))
        reverse(G[u].begin(), G[u].end());
      int flag = 0;
      vector<int> tG;
      for (auto v : G[u]) {
        int sta = getstate(v);
        if (sta == 0) {
          if (flag == 1) flag = 2;
          tG.push_back(v);
        } else if (sta == 2) {
          if (flag == 0)
            flag = 1;
          else if (flag == 2)
            return fail = 2, 0;
          tG.push_back(v);
        } else {
          int p1;
          if (flag == 0)
            flag = 1, p1 = check(v, 2);
          else if (flag == 1)
            flag = 2, p1 = check(v, 1);
          else
            return fail = 3, 0;
          for (auto x : G[v]) tG.push_back(x);
        }
      }
      if (t && flag == 2) return fail = 4, 0;
      if (t == 1) reverse(tG.begin(), tG.end());
      G[u] = tG;
      return u;
    }
  }
  int res;
  void dfs_permutation(int u) {
    if (u <= n) {
      return;
    }
    if (!ty[u]) {
      res = (long long)res * fac[G[u].size()] % 998244353;
    } else if (G[u].size() != 1) {
      res = (long long)res * 2 % 998244353;
    }
    for (auto v : G[u]) {
      dfs_permutation(v);
    }
  }
  int get_permutation() {
    res = 1;
    dfs_permutation(n + 1);
    return res;
  }
  void restrict(vector<int> res) {
    for (int i = 1; i <= n; i++) s[i] = 0;
    for (auto x : res) s[x] = 1;
    dfs(n + 1);
    check(n + 1, 0);
  }
};
int main() {
  fac[0] = 1;
  int i;
  for (i = (1); i < (128); i++) fac[i] = (fac[i - 1] * i) % 998244353;
  int n, m;
  cin >> n >> m;
  PQ_tree *pq = new PQ_tree;
  pq->init(n);
  for (i = (0); i < (m); i++) {
    vector<int> res;
    int amt;
    cin >> amt;
    int j;
    for (j = (0); j < (amt); j++) {
      int a;
      cin >> a;
      res.push_back(a);
    }
    pq->restrict(res);
  }
  if (pq->fail) {
    cout << 0;
  } else
    cout << pq->get_permutation();
}
