import sys

import collections



sys.setrecursionlimit(10 ** 8)

ni = lambda: int(sys.stdin.readline())

nm = lambda: list(map(int, sys.stdin.readline().split()))

nl = lambda: list(nm())

ns = lambda: sys.stdin.readline().rstrip()





def solve():

    N, M = nm()

    uf = UnionFind(M)

    lang_spoken = set()

    for i in range(N):

        k, *lang = nm()

        assert len(lang) == k

        roots = {uf.find(l - 1) for l in lang}

        lang_spoken |= roots

        roots = list(roots)

        x = roots[0]

        for y in roots[1:]:

            uf.union(x, y)

    lang1 = next(iter(lang_spoken))

    return uf.size(lang1) == len(lang_spoken)





class UnionFind:

    def __init__(self, n):

        # total number of nodes.

        self.n = n

        # node id -> root node id

        self._root_table = list(range(n))

        # root node id -> group size

        self._size_table = [1] * n



    def find(self, x):

        """Returns x's root node id."""

        r = self._root_table[x]

        if r != x:

            # Update the cache on query.

            r = self._root_table[x] = self.find(r)

        return r



    def union(self, x, y):

        """Merges two groups."""

        x = self.find(x)

        y = self.find(y)



        if x == y:

            return



        # Ensure that x is the larger (or equal) group.

        if self._size_table[x] < self._size_table[y]:

            x, y = y, x



        self._size_table[x] += self._size_table[y]

        self._root_table[y] = x



    def size(self, x):

        return self._size_table[self.find(x)]



    def same(self, x, y):

        return self.find(x) == self.find(y)



    def members(self, x):

        root = self.find(x)

        return [i for i in range(self.n) if self.find(i) == root]



    def roots(self):

        return [i for i, x in enumerate(self._root_table) if x == i]



    def group_count(self):

        return len(self.roots())



    def all_group_members(self):

        return {r: self.members(r) for r in self.roots()}



    def __str__(self):

        return "\n".join("{}: {}".format(r, self.members(r)) for r in self.roots())





print((["NO", "YES"][solve()]))

import sys

import collections



sys.setrecursionlimit(10 ** 8)

ni = lambda: int(sys.stdin.readline())

nm = lambda: list(map(int, sys.stdin.readline().split()))

nl = lambda: list(nm())

ns = lambda: sys.stdin.readline().rstrip()





def solve():

    N, M = nm()

    uf = UnionFind(M)

    lang_spoken = set()

    for i in range(N):

        k, *lang = nm()

        assert len(lang) == k

        roots = {uf.find(l - 1) for l in lang}

        lang_spoken |= roots

        rit = iter(roots)

        x = next(rit)

        for y in rit:

            uf.union(x, y)

    lang1 = next(iter(lang_spoken))

    return uf.size(lang1) == len(lang_spoken)





class UnionFind:

    def __init__(self, n):

        # total number of nodes.

        self.n = n

        # node id -> root node id

        self._root_table = list(range(n))

        # root node id -> group size

        self._size_table = [1] * n



    def find(self, x):

        """Returns x's root node id."""

        r = self._root_table[x]

        if r != x:

            # Update the cache on query.

            r = self._root_table[x] = self.find(r)

        return r



    def union(self, x, y):

        """Merges two groups."""

        x = self.find(x)

        y = self.find(y)



        if x == y:

            return



        # Ensure that x is the larger (or equal) group.

        if self._size_table[x] < self._size_table[y]:

            x, y = y, x



        self._size_table[x] += self._size_table[y]

        self._root_table[y] = x



    def size(self, x):

        return self._size_table[self.find(x)]



    def same(self, x, y):

        return self.find(x) == self.find(y)



    def members(self, x):

        root = self.find(x)

        return [i for i in range(self.n) if self.find(i) == root]



    def roots(self):

        return [i for i, x in enumerate(self._root_table) if x == i]



    def group_count(self):

        return len(self.roots())



    def all_group_members(self):

        return {r: self.members(r) for r in self.roots()}



    def __str__(self):

        return "\n".join("{}: {}".format(r, self.members(r)) for r in self.roots())





print((["NO", "YES"][solve()]))

import sys
import collections

sys.setrecursionlimit(10**8)
ni = lambda: int(sys.stdin.readline())
nm = lambda: list(map(int, sys.stdin.readline().split()))
nl = lambda: list(nm())
ns = lambda: sys.stdin.readline().rstrip()


def solve():
    N, M = nm()
    uf = UnionFind(M)
    lang_spoken = set()
    for i in range(N):
        k, *lang = nm()
        assert len(lang) == k
        roots = {uf.find(l - 1) for l in lang}
        lang_spoken |= roots
        roots = list(roots)
        x = roots[0]
        for y in roots[1:]:
            uf.union(x, y)
    lang1 = next(iter(lang_spoken))
    return uf.size(lang1) == len(lang_spoken)


class UnionFind:
    def __init__(self, n):
        # total number of nodes.
        self.n = n
        # node id -> root node id
        self._root_table = list(range(n))
        # root node id -> group size
        self._size_table = [1] * n

    def find(self, x):
        """Returns x's root node id."""
        r = self._root_table[x]
        if r != x:
            # Update the cache on query.
            r = self._root_table[x] = self.find(r)
        return r

    def union(self, x, y):
        """Merges two groups."""
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        # Ensure that x is the larger (or equal) group.
        if self._size_table[x] < self._size_table[y]:
            x, y = y, x
        self._size_table[x] += self._size_table[y]
        self._root_table[y] = x

    def size(self, x):
        return self._size_table[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self._root_table) if x == i]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return "\n".join("{}: {}".format(r, self.members(r)) for r in self.roots())


print((["NO", "YES"][solve()]))

import sys
import collections

sys.setrecursionlimit(10**8)
ni = lambda: int(sys.stdin.readline())
nm = lambda: list(map(int, sys.stdin.readline().split()))
nl = lambda: list(nm())
ns = lambda: sys.stdin.readline().rstrip()


def solve():
    N, M = nm()
    uf = UnionFind(M)
    lang_spoken = set()
    for i in range(N):
        k, *lang = nm()
        assert len(lang) == k
        roots = {uf.find(l - 1) for l in lang}
        lang_spoken |= roots
        rit = iter(roots)
        x = next(rit)
        for y in rit:
            uf.union(x, y)
    lang1 = next(iter(lang_spoken))
    return uf.size(lang1) == len(lang_spoken)


class UnionFind:
    def __init__(self, n):
        # total number of nodes.
        self.n = n
        # node id -> root node id
        self._root_table = list(range(n))
        # root node id -> group size
        self._size_table = [1] * n

    def find(self, x):
        """Returns x's root node id."""
        r = self._root_table[x]
        if r != x:
            # Update the cache on query.
            r = self._root_table[x] = self.find(r)
        return r

    def union(self, x, y):
        """Merges two groups."""
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        # Ensure that x is the larger (or equal) group.
        if self._size_table[x] < self._size_table[y]:
            x, y = y, x
        self._size_table[x] += self._size_table[y]
        self._root_table[y] = x

    def size(self, x):
        return self._size_table[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self._root_table) if x == i]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return "\n".join("{}: {}".format(r, self.members(r)) for r in self.roots())


print((["NO", "YES"][solve()]))

-        roots = list(roots) -        x = roots[0] -        for y in roots[1:]: +        rit = iter(roots) +        x = next(rit) +        for y in rit: