#include <bits/stdc++.h>
// #include "/usr/local/include/cxx_pretty_print/cxx_pretty_print.hpp"

using namespace std;
/* type aliases */
using i32 = int32_t; using ui32 = uint32_t; using i64 = int64_t; using ui64 = uint64_t; using i128 = __int128;
using f32 = float;   using f64  = double;   using f80 = long double;
using PII = pair<i32, i32>; using PLL = pair<i64, i64>; using PFF = pair<f32, f32>; using PDD = pair<f64, f64>;
using VB  = vector<bool>;   using VS  = vector<string>;
using VI  = vector<i32>;    using VL  = vector<i64>;    using VF  = vector<f32>;    using VD  = vector<f64>;
using VVI = vector<VI>;     using VVL = vector<VL>;     using VVF = vector<VF>;     using VVD = vector<VD>;
using VPI = vector<PII>;    using VPL = vector<PLL>;    using VPF = vector<PFF>;    using VPD = vector<PDD>;
/* constants */
constexpr i64 mod = 1000000007;                                // 10^9+7
constexpr i64 inf = 9000000000000000000;                       // 9*10^18
/* macros */
#define MAX(c)        *max_element(ALL(c))
#define MIN(c)        *min_element(ALL(c))
#define SIZE(c)       (i64)((c).size())                        // unsined int (size_t) -> int64_t
#define ALL(c)        (c).begin(), (c).end()
#define RALL(c)       (c).rbegin(), (c).rend()
#define REP(i, n)     for (i64 i=0; i<(i64)(n); ++i)           // 0 to n-1
#define REPD(i, n)    for (i64 i=(i64)(n)-1; i>=0; --i)        // n-1 to 0
/* debug */
#define debug(x)      cerr << #x << ": " << (x) << '\n'
#define debugvec(v)   for (size_t _ = 0; _ < (v).size(); ++_) {cerr << #v << '[' << _ << "]: " << (v)[_] << '\n';}
/* useful output */
auto print = [](auto x) {cout << (x) << '\n';};
template <class T> void printvec(T v, string sep=" ", string end="\n") {REP(i, SIZE(v)-1) cout << v[i] << sep; cout << v[SIZE(v)-1] << end;}
/* useful input */
VS splitstr(string s, char delim=' ') {VS v; string buf; stringstream ss{s}; while (getline(ss, buf, delim)) v.push_back(buf); return v;}



struct UFtree {
    i64 n;
    VL par;
    VL rank;
    VL group_size;
    UFtree(i64 num_of_elm) : n(num_of_elm), par(num_of_elm, 0), rank(num_of_elm, 0), group_size(num_of_elm, 1) {
        for (i64 i = 0; i < num_of_elm; ++i) par[i] = i;
    }

    i64 find_root(i64 x) {
        i64 parent = par[x];
        if (parent == x) {
            return x;
        } else {
            i64 root = find_root(parent);
            // 経路圧縮
            par[x] = root;
            return root;
        }
    }

    bool is_same(i64 x, i64 y) {
        return (find_root(x) == find_root(y));
    }

    bool unite(i64 x, i64 y) {
        i64 shallow = find_root(x);
        i64 deep = find_root(y);
        if (rank[shallow] > rank[deep]) {
            swap(shallow, deep);
        }
        if (shallow == deep) {
            return false;
        } else {
            par[shallow] = deep;
            group_size[deep] += group_size[shallow];
            if (rank[shallow] == rank[deep]) rank[deep]++;
            return true;
        }
    }

    i64 akin_num(i64 x) {
        i64 root = find_root(x);
        return group_size[root];
    }
};



void Main() {
    i32 n, m;
    cin >> n >> m;
    UFtree uf(m);
    VI represent(n, 0);
    REP(i, n) {
        i32 k;
        cin >> k;
        i32 prev;
        cin >> prev;
        represent.at(i) = prev;    // 人 i を代表する言語一つ抜き出してメモっておく
        REP(j, k-1) {
            i32 lang;
            cin >> lang;
            uf.unite(prev, lang);
            prev = lang;
        }
    }
    // debugvec(uf.par);
    i32 group = -1;
    REP(i, n) {
        i32 g = uf.find_root(represent.at(i));
        if (group == -1) {
            group = g;
        } else if (group != g) {
            print("NO");
            return;
        }
    }
    print("YES");
    return;
}


signed main() {
    cin.tie(nullptr);                                         // cin と cout の tie を切り高速化
    cout << fixed << setprecision(15);                        // 浮動小数点数が 1e-006 のように表示されることを防ぐ
    Main();
}

Measured memory_bytes: 5808