description: A subarray of array a from index l to the index r is the array [a_l, a_{l+1}, ..., a_{r}]. The number of occurrences of the array b in the array a is the number of subarrays of a such that they are equal to b.
  
  You are given n arrays A_1, A_2, ..., A_n; the elements of these arrays are integers from 1 to k. You have to build an array a consisting of m integers from 1 to k in such a way that, for every given subarray A_i, the number of occurrences of A_i in the array a is not less than the number of occurrences of each non-empty subarray of A_i in a. Note that if A_i doesn't occur in a, and no subarray of A_i occurs in a, this condition is still met for A_i.
  
  Your task is to calculate the number of different arrays a you can build, and print it modulo 998244353.
  
  Input
  
  The first line contains three integers n, m and k (1 ≤ n, m, k ≤ 3 ⋅ 10^5) — the number of the given arrays, the desired length of the array a, and the upper bound on the values in the arrays.
  
  Then n lines follow. The i-th line represents the array A_i. The first integer in the i-th line is c_i (1 ≤ c_i ≤ m) — the number of elements in A_i; then, c_i integers from 1 to k follow — the elements of the array A_i.
  
  Additional constraint on the input: ∑_{i=1}^n c_i ≤ 3 ⋅ 10^5; i. e., the number of elements in the given arrays in total does not exceed 3 ⋅ 10^5.
  
  Output
  
  Print one integer — the number of different arrays a you can build, taken modulo 998244353.
  
  Examples
  
  Input
  
  
  2 4 3
  2 1 2
  1 3
  
  
  Output
  
  
  5
  
  
  Input
  
  
  2 4 3
  2 1 2
  3 3 2 1
  
  
  Output
  
  
  0
  
  
  Input
  
  
  1 42 1337
  2 13 31
  
  
  Output
  
  
  721234447
solution: #include <bits/stdc++.h>
  using namespace std;
  template <typename A, typename B>
  string to_string(const pair<A, B>& p);
  template <typename A, typename B, typename C>
  string to_string(const tuple<A, B, C>& t);
  template <typename A, typename B, typename C, typename D>
  string to_string(const tuple<A, B, C, D>& t);
  string to_string(const string& s) { return '"' + s + '"'; }
  string to_string(const char& c) { return string("'") + c + "'"; }
  string to_string(const char* c) { return to_string(string(c)); }
  string to_string(const bool& b) { return (b ? "true" : "false"); }
  string to_string(const vector<bool>& v) {
    string res = "{";
    for (int i = 0; i < (int)v.size(); ++i) {
      if (i > 0) {
        res += ", ";
      }
      res += to_string(v[i]);
    }
    res += "}";
    return res;
  }
  template <size_t T>
  string to_string(const bitset<T>& bs) {
    return bs.to_string();
  }
  template <typename T>
  string to_string(queue<T> q) {
    string res = "{";
    size_t sz = q.size();
    while (sz--) {
      T cur = q.front();
      q.pop();
      if ((int)res.size() > 1) {
        res += ", ";
      }
      res += to_string(cur);
    }
    res += "}";
    return res;
  }
  template <typename T, class C>
  string to_string(priority_queue<T, vector<T>, C> pq) {
    string res = "{";
    while (!pq.empty()) {
      T cur = pq.top();
      pq.pop();
      if ((int)res.size() > 1) {
        res += ", ";
      }
      res += to_string(cur);
    }
    res += "}";
    return res;
  }
  template <typename T>
  string to_string(const T& v) {
    string res = "{";
    for (auto& el : v) {
      if ((int)res.size() > 1) {
        res += ", ";
      }
      res += to_string(el);
    }
    res += "}";
    return res;
  }
  template <typename A, typename B>
  string to_string(const pair<A, B>& p) {
    return '(' + to_string(p.first) + ", " + to_string(p.second) + ')';
  }
  template <typename A, typename B, typename C>
  string to_string(const tuple<A, B, C>& t) {
    return '(' + to_string(get<0>(t)) + ", " + to_string(get<1>(t)) + ", " +
           to_string(get<2>(t)) + ')';
  }
  template <typename A, typename B, typename C, typename D>
  string to_string(const tuple<A, B, C, D>& t) {
    return '(' + to_string(get<0>(t)) + ", " + to_string(get<1>(t)) + ", " +
           to_string(get<2>(t)) + ", " + to_string(get<3>(t)) + ')';
  }
  void debug_out(int size, bool first, string name) {
    vector<string> tmp = {name};
    vector<int> tmp2 = {size, first};
    cerr << endl;
  }
  constexpr int buffer_size = 255;
  template <typename Head, typename... Tail>
  void debug_out(int size, bool first, string name, Head H, Tail... T) {
    string tmp;
    int off = 0;
    while ((!name.empty() && name[0] != ',') || off != 0) {
      tmp += name[0];
      name.erase(name.begin());
      char c = tmp.back();
      if (c == '{' || c == '(') {
        ++off;
      } else if (c == '}' || c == ')') {
        --off;
      }
    }
    if (!name.empty()) {
      name.erase(name.begin());
    }
    if (tmp[0] == ' ') {
      tmp.erase(tmp.begin());
    }
    string buff = to_string(H);
    if ((int)buff.size() + size + (int)tmp.size() > buffer_size - 5 && !first) {
      cerr << '\n';
      size = 0;
    }
    cerr << '[' << tmp << ": " << buff << "] ";
    debug_out(((int)buff.size() + size + (int)tmp.size() + 5) % buffer_size,
              false, name, T...);
  }
  template <typename T>
  T inverse(T a, T m) {
    T u = 0, v = 1;
    while (a != 0) {
      T t = m / a;
      m -= t * a;
      swap(a, m);
      u -= t * v;
      swap(u, v);
    }
    assert(m == 1);
    return u;
  }
  template <typename T>
  class Modular {
   public:
    using Type = typename decay<decltype(T::value)>::type;
    constexpr Modular() : value() {}
    template <typename U>
    Modular(const U& x) {
      value = normalize(x);
    }
    template <typename U>
    static Type normalize(const U& x) {
      Type v;
      if (-mod() <= x && x < mod())
        v = static_cast<Type>(x);
      else
        v = static_cast<Type>(x % mod());
      if (v < 0) v += mod();
      return v;
    }
    const Type& operator()() const { return value; }
    template <typename U>
    explicit operator U() const {
      return static_cast<U>(value);
    }
    constexpr static Type mod() { return T::value; }
    Modular& operator+=(const Modular& other) {
      if ((value += other.value) >= mod()) value -= mod();
      return *this;
    }
    Modular& operator-=(const Modular& other) {
      if ((value -= other.value) < 0) value += mod();
      return *this;
    }
    template <typename U>
    Modular& operator+=(const U& other) {
      return *this += Modular(other);
    }
    template <typename U>
    Modular& operator-=(const U& other) {
      return *this -= Modular(other);
    }
    Modular& operator++() { return *this += 1; }
    Modular& operator--() { return *this -= 1; }
    Modular operator++(int) {
      Modular result(*this);
      *this += 1;
      return result;
    }
    Modular operator--(int) {
      Modular result(*this);
      *this -= 1;
      return result;
    }
    Modular operator-() const { return Modular(-value); }
    template <typename U = T>
    typename enable_if<is_same<typename Modular<U>::Type, int>::value,
                       Modular>::type&
    operator*=(const Modular& rhs) {
      value = normalize(static_cast<int64_t>(value) *
                        static_cast<int64_t>(rhs.value));
      return *this;
    }
    template <typename U = T>
    typename enable_if<is_same<typename Modular<U>::Type, int64_t>::value,
                       Modular>::type&
    operator*=(const Modular& rhs) {
      int64_t q = static_cast<int64_t>(static_cast<long double>(value) *
                                       rhs.value / mod());
      value = normalize(value * rhs.value - q * mod());
      return *this;
    }
    template <typename U = T>
    typename enable_if<!is_integral<typename Modular<U>::Type>::value,
                       Modular>::type&
    operator*=(const Modular& rhs) {
      value = normalize(value * rhs.value);
      return *this;
    }
    Modular& operator/=(const Modular& other) {
      return *this *= Modular(inverse(other.value, mod()));
    }
    template <typename U>
    friend const Modular<U>& abs(const Modular<U>& v) {
      return v;
    }
    template <typename U>
    friend bool operator==(const Modular<U>& lhs, const Modular<U>& rhs);
    template <typename U>
    friend bool operator<(const Modular<U>& lhs, const Modular<U>& rhs);
    template <typename U>
    friend std::istream& operator>>(std::istream& stream, Modular<U>& number);
  
   private:
    Type value;
  };
  template <typename T>
  bool operator==(const Modular<T>& lhs, const Modular<T>& rhs) {
    return lhs.value == rhs.value;
  }
  template <typename T, typename U>
  bool operator==(const Modular<T>& lhs, U rhs) {
    return lhs == Modular<T>(rhs);
  }
  template <typename T, typename U>
  bool operator==(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) == rhs;
  }
  template <typename T>
  bool operator!=(const Modular<T>& lhs, const Modular<T>& rhs) {
    return !(lhs == rhs);
  }
  template <typename T, typename U>
  bool operator!=(const Modular<T>& lhs, U rhs) {
    return !(lhs == rhs);
  }
  template <typename T, typename U>
  bool operator!=(U lhs, const Modular<T>& rhs) {
    return !(lhs == rhs);
  }
  template <typename T>
  bool operator<(const Modular<T>& lhs, const Modular<T>& rhs) {
    return lhs.value < rhs.value;
  }
  template <typename T>
  Modular<T> operator+(const Modular<T>& lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) += rhs;
  }
  template <typename T, typename U>
  Modular<T> operator+(const Modular<T>& lhs, U rhs) {
    return Modular<T>(lhs) += rhs;
  }
  template <typename T, typename U>
  Modular<T> operator+(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) += rhs;
  }
  template <typename T>
  Modular<T> operator-(const Modular<T>& lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) -= rhs;
  }
  template <typename T, typename U>
  Modular<T> operator-(const Modular<T>& lhs, U rhs) {
    return Modular<T>(lhs) -= rhs;
  }
  template <typename T, typename U>
  Modular<T> operator-(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) -= rhs;
  }
  template <typename T>
  Modular<T> operator*(const Modular<T>& lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) *= rhs;
  }
  template <typename T, typename U>
  Modular<T> operator*(const Modular<T>& lhs, U rhs) {
    return Modular<T>(lhs) *= rhs;
  }
  template <typename T, typename U>
  Modular<T> operator*(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) *= rhs;
  }
  template <typename T>
  Modular<T> operator/(const Modular<T>& lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) /= rhs;
  }
  template <typename T, typename U>
  Modular<T> operator/(const Modular<T>& lhs, U rhs) {
    return Modular<T>(lhs) /= rhs;
  }
  template <typename T, typename U>
  Modular<T> operator/(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) /= rhs;
  }
  template <typename T, typename U>
  Modular<T> power(const Modular<T>& a, const U& b) {
    assert(b >= 0);
    Modular<T> x = a, res = 1;
    U p = b;
    while (p > 0) {
      if (p & 1) res *= x;
      x *= x;
      p >>= 1;
    }
    return res;
  }
  template <typename T>
  bool IsZero(const Modular<T>& number) {
    return number() == 0;
  }
  template <typename T>
  string to_string(const Modular<T>& number) {
    return to_string(number());
  }
  template <typename T>
  std::ostream& operator<<(std::ostream& stream, const Modular<T>& number) {
    return stream << number();
  }
  template <typename T>
  std::istream& operator>>(std::istream& stream, Modular<T>& number) {
    typename common_type<typename Modular<T>::Type, int64_t>::type x;
    stream >> x;
    number.value = Modular<T>::normalize(x);
    return stream;
  }
  constexpr int md = 998244353;
  using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>;
  struct dsu {
    vector<int> par, sz;
    dsu(int n) {
      par.resize(n);
      sz.resize(n, 1);
      iota(par.begin(), par.end(), 0);
    }
    inline int get(int v) { return par[v] = (v == par[v] ? v : get(par[v])); }
    inline bool unite(int x, int y) {
      x = get(x);
      y = get(y);
      if (x == y) return false;
      sz[x] += sz[y];
      par[y] = x;
      return true;
    }
    int size(int v) { return sz[get(v)]; }
    bool same(int x, int y) { return get(x) == get(y); }
  };
  int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int n, m, k;
    cin >> n >> m >> k;
    vector<vector<int>> g(k);
    for (int i = 0; i < n; ++i) {
      int s;
      cin >> s;
      vector<int> a(s);
      for (int j = 0; j < s; ++j) {
        cin >> a[j];
        --a[j];
      }
      for (int j = 0; j < s - 1; ++j) {
        g[a[j]].push_back(a[j + 1]);
      }
    }
    dsu d(k);
    vector<int> indeg(k);
    vector<int> outdeg(k);
    for (int i = 0; i < k; ++i) {
      sort(g[i].begin(), g[i].end());
      g[i].erase(unique(g[i].begin(), g[i].end()), g[i].end());
      for (auto u : g[i]) {
        ++indeg[u];
        d.unite(i, u);
      }
      outdeg[i] = int(g[i].size());
    }
    vector<vector<int>> gs(k);
    for (int i = 0; i < k; ++i) {
      gs[d.get(i)].push_back(i);
    }
    vector<int> ct(k + 1);
    for (int i = 0; i < k; ++i) {
      array<int, 4> c = {};
      for (auto v : gs[i]) {
        if (indeg[v] > 1 || outdeg[v] > 1) {
          continue;
        }
        ++c[indeg[v] + 2 * outdeg[v]];
      }
      int size = int(gs[i].size());
      if ((c[1] == 1 && c[2] == 1 && c[3] == size - 2) || size == 1) {
        ++ct[size];
      }
    }
    vector<int> add;
    for (int i = 0; i <= k; ++i) {
      if (ct[i] > 0) {
        add.push_back(i);
      }
    }
    vector<Mint> dp(m + 1);
    dp[0] = 1;
    for (int i = 1; i <= m; ++i) {
      for (auto j : add) {
        dp[i] += (i >= j ? dp[i - j] * ct[j] : 0);
      }
    }
    cout << dp[m] << '\n';
  }
  