The "BerCorp" company has got *n* employees. These employees can use *m* approved official languages for the formal correspondence. The languages are numbered with integers from 1 to *m*. For each employee we have the list of languages, which he knows. This list could be empty, i. e. an employee may know no official languages. But the employees are willing to learn any number of official languages, as long as the company pays their lessons. A study course in one language for one employee costs 1 berdollar.

Find the minimum sum of money the company needs to spend so as any employee could correspond to any other one (their correspondence can be indirect, i. e. other employees can help out translating).
The first line contains two integers *n* and *m* (2 ≤ *n*, *m* ≤ 100) — the number of employees and the number of languages.

Then *n* lines follow — each employee's language list. At the beginning of the *i*-th line is integer *k*<sub class="lower-index">*i*</sub> (0 ≤ *k*<sub class="lower-index">*i*</sub> ≤ *m*) — the number of languages the *i*-th employee knows. Next, the *i*-th line contains *k*<sub class="lower-index">*i*</sub> integers — *a*<sub class="lower-index">*ij*</sub> (1 ≤ *a*<sub class="lower-index">*ij*</sub> ≤ *m*) — the identifiers of languages the *i*-th employee knows. It is guaranteed that all the identifiers in one list are distinct. Note that an employee may know zero languages.

The numbers in the lines are separated by single spaces.
#!/usr/bin/env python
from __future__ import division, print_function
 
import os
from re import M
import sys
import collections
from io import BytesIO, IOBase
 
if sys.version_info[0] < 3:
    from __builtin__ import xrange as range
    from future_builtins import ascii, filter, hex, map, oct, zip
 
 
def main():
    pass
 
 
# region fastio
 
BUFSIZE = 8192
 
 
class FastIO(IOBase):
    newlines = 0
 
    def __init__(self, file):
        self._file = file
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = "x" in file.mode or "r" not in file.mode
        self.write = self.buffer.write if self.writable else None
 
    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)	
        self.newlines = 0
        return self.buffer.read()
 
    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b"\n") + (not b)
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines -= 1
        return self.buffer.readline()
 
    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            self.buffer.truncate(0), self.buffer.seek(0)
 
 
class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode("ascii"))
        self.read = lambda: self.buffer.read().decode("ascii")
        self.readline = lambda: self.buffer.readline().decode("ascii")
 
 
def print(*args, **kwargs):
    """Prints the values to a stream, or to sys.stdout by default."""
    sep, file = kwargs.pop("sep", " "), kwargs.pop("file", sys.stdout)
    at_start = True
    for x in args:
        if not at_start:
            file.write(sep)
        file.write(str(x))
        at_start = False
    file.write(kwargs.pop("end", "\n"))
    if kwargs.pop("flush", False):
        file.flush()
 
 
if sys.version_info[0] < 3:
    sys.stdin, sys.stdout = FastIO(sys.stdin), FastIO(sys.stdout)
else:
    sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
 
input = lambda: sys.stdin.readline().rstrip("\r\n")


################ segmentation tree ################

from math import inf, log2

class SegmentTree:
    def __init__(self, array, func=max):
        self.n = len(array)
        self.size = 2**(int(log2(self.n-1))+1) if self.n != 1 else 1
        self.func = func
        self.default = 0 if self.func != min else inf
        self.data = [self.default] * (2 * self.size)
        self.process(array)

    def process(self, array):
        self.data[self.size : self.size+self.n] = array
        for i in range(self.size-1, -1, -1):
            self.data[i] = self.func(self.data[2*i], self.data[2*i+1])

    def query(self, alpha, omega):
        """Returns the result of function over the range (inclusive)!"""
        if alpha == omega:
            return self.data[alpha + self.size]
        res = self.default
        alpha += self.size
        omega += self.size + 1
        while alpha < omega:
            if alpha & 1:
                res = self.func(res, self.data[alpha])
                alpha += 1
            if omega & 1:
                omega -= 1
                res = self.func(res, self.data[omega])
            alpha >>= 1
            omega >>= 1
        return res

    def update(self, index, value):
        """Updates the element at index to given value!"""
        index += self.size
        self.data[index] = value
        index >>= 1
        while index:
            self.data[index] = self.func(self.data[2*index], self.data[2*index+1])
            index >>= 1
def binex(base, pow):
    ans=1
    while pow>0:
        if pow&1:
            ans*=base
        base*=base
        pow//=2
    return ans

def binexmod(base, pow, mod):
    base%=mod
    ans=1
    while pow>0:
        if pow&1:
            ans=(ans*base)%mod
        base=(base*base)%mod
        pow//=2
    return ans%mod

from bisect import bisect_left, bisect_right

class Result:
    def __init__(self, index, value):
        self.index = index
        self.value = value

class BinarySearch:
    def __init__(self):
        pass

    @staticmethod
    def greater_than(num: int, func, size: int = 1):
        """Searches for smallest element greater than num!"""
        if isinstance(func, list):
            index = bisect_right(func, num)
            if index == len(func):
                return Result(None, None)
            else:
                return Result(index, func[index])
        else:
            alpha, omega = 0, size - 1
            if func(omega) <= num:
                return Result(None, None)
            while alpha < omega:
                if func(alpha) > num:
                    return Result(alpha, func(alpha))
                if omega == alpha + 1:
                    return Result(omega, func(omega))
                mid = (alpha + omega) // 2
                if func(mid) > num:
                    omega = mid
                else:
                    alpha = mid

    @staticmethod
    def less_than(num: int, func, size: int = 1):
        """Searches for largest element less than num!"""
        if isinstance(func, list):
            index = bisect_left(func, num) - 1
            if index == -1:
                return Result(None, None)
            else:
                return Result(index, func[index])
        else:
            alpha, omega = 0, size - 1
            if func(alpha) >= num:
                return Result(None, None)
            while alpha < omega:
                if func(omega) < num:
                    return Result(omega, func(omega))
                if omega == alpha + 1:
                    return Result(alpha, func(alpha))
                mid = (alpha + omega) // 2
                if func(mid) < num:
                    alpha = mid
                else:
                    omega = mid

# ########### binarysearch ################
# def binarysearch(x,list,lo,up):
#     while lo < up:
#         mid = (up + lo) // 2
#         if list[mid] == x:
#             return mid
#         elif x < list[mid]:
#             up = mid
#         else:
#             lo = mid +1
#     # lo -> len
#     return lo

# ########### factorial ################
# [int(j) for j in input().split()]
# fact = {1:1}
# def factorial(n):
#     if n in fact:
#         return fact[n]
#     else:
#         a = n*factorial(n-1)
#         fact[n] = a
#         return a

# for i in range(50):
#     factorial(i)

# ########### sqrt ################
# def floorSqrt(x):
#     if x in roo:
#         return roo[x]
#     if (x == 0 or x == 1):
#         return x
#     i = 1; result = 1
#     while (result <= x):
     
#         i += 1
#         result = i * i
#     roo[x] = i -1
#     return i - 1



# ########### power ################
# def power(x, y) :
#     if y in powe:
#         return powe[y]
#     res = 1
#     x = x % 1000000007
#     if (x == 0) :
#         return 0
 
#     while (y > 0) :
#         if ((y & 1) == 1) :
#             res = (res * x) % 1000000007
#         y = y >> 1    
#         x = (x * x) % 1000000007
#     return res

# ########### power ################
# def gcd(a,b):
#     if a == 0:
#         return b
#     return gcd(b%a,a)
# def lcm(a,b):
#     return ((a*b)//gcd(a,b))

# def isPowerOf2(n):
#     return n and not (n&(n-1))

# def countBits(n):
#     count = 0
#     while n:
#         count += 1
#         n = n & (n - 1)
#     return count
############ ---- Input Functions ---- ############
def inpi():
    return(int(input()))
def inpl():
    return(list(map(int,input().split())))
def inps():
    s = input()
    return(list(s[:len(s)]))
def inpm():
    return(map(int,input().split()))
def inpst():
    return(set(map(int,input().split())))
# from queue import Queue
# def bfs(adjList,s):
#     visited = {}
#     level = {}
#     parent = {}
#     queue = Queue()
#     bfs_traversal_output = []
#     for node in adjList.keys():
#         visited[node] = False
#         parent[node] = None
#         level[node] = -1
#     visited[s] = True
#     level[s] = 0
#     queue.put(s)

#     while not queue.empty():
#         u = queue.get()
#         bfs_traversal_output.append(u)

#         for v in adjList[u]:
#             if not visited[v]:
#                 visited[v] = True
#                 parent[v] = u
#                 level[v] = level[u]+1
#                 queue.put(v)
#     return bfs_traversal_output

class DisjointSetUnion:
    def __init__(self, n):
        self.parent = [*range(n+1)]
        self.size = [1]*(n+1)
        self.min, self.max = [*range(n+1)], [*range(n+1)]
        self.count = n

    def get(self, a):
        """Returns the identifier (parent) of the set to which a belongs to!"""
        if self.parent[a] == a:
            return a
        x = a
        while a != self.parent[a]:
            a = self.parent[a]
        while x != self.parent[x]:
            self.parent[x], x = a, self.parent[x]
        return a

    def union(self, a, b):
        """Join two sets that contain a and b!"""
        a, b = self.get(a), self.get(b)
        if a != b:
            if self.size[a] > self.size[b]:
                a, b = b, a
            self.parent[a] = b
            self.size[b] += self.size[a]
            self.min[b] = min(self.min[a], self.min[b])
            self.max[b] = max(self.max[a], self.max[b])
            self.count -= 1

    def count_sets(self):
        """Returns the number of disjoint sets!"""
        return self.count


def solve():
    n,m = inpl()
    dsu = DisjointSetUnion(m + n)
    s = set()
    for i in range(n):
        l = inpl()
        for j in range(l[0]):
            s.add(l[j+1])
            dsu.union(i,n+l[j+1]-1)
    print(dsu.count_sets()-(m-len(s))-1)
    # print(dsu.count_sets(), m, len(s))




t = 1
# t = inpi()
for _ in range(t):
    solve()

    