import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) throws IOException{
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        A170 solver = new A170();
        solver.solve(1, in, out);
        out.close();
    }

    static class A170 {
        int[][] graph = new int[102][102];
        boolean[] visited = new boolean[102];

        public void solve(int testNumber, InputReader in, PrintWriter out) throws IOException {

            StringTokenizer st = new StringTokenizer(in.nextLine());
            int aris = Integer.parseInt(st.nextToken());
            int n = Integer.parseInt(st.nextToken());
            int[] hash = new int[102];
            int count = 0;
            boolean enter = false;

            for (int i = 0; i < aris; i++) {

                String[] s = in.nextLine().split(" ");
                int cur = Integer.parseInt(s[0]);
                if (cur == 0) {
                    count++;
                } else {
                    enter = true;
                    int aux = Integer.parseInt(s[1]);
                    hash[aux]++;
                    for (int j = 1; j < cur; j++) {
                        int ac = Integer.parseInt(s[j + 1]);
                        hash[ac]++;
                        graph[aux][ac] = 1;
                        graph[ac][aux] = 1;
                        aux = ac;
                    }
                }

            }
            if (enter) {

                for (int i = 1; i <= aris; i++) {

                    if (!visited[i] && hash[i] > 0) {
                        dfs(i);
                        count++;
                    }

                }
                out.println(count - 1);
            } else
                out.println(n);

        }

        void dfs(int nod) {
            visited[nod] = true;
            for (int i = 0; i < graph[nod].length; i++) {

                if (!visited[i] && graph[nod][i] == 1) {
                    dfs(i);
                }
            }

        }

    }

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String nextLine() throws IOException {
            return reader.readLine();
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Set;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.Queue;
import java.io.BufferedReader;
import java.util.LinkedList;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        A170 solver = new A170();
        solver.solve(1, in, out);
        out.close();
    }

    static class A170 {
        int[][] graph;
        Set<Integer> set;
        Queue<Integer> q;

        public void solve(int testNumber, InputReader in, PrintWriter out) throws IOException {

            StringTokenizer st = new StringTokenizer(in.nextLine());
            int aris = Integer.parseInt(st.nextToken());
            int n = Integer.parseInt(st.nextToken());
            graph = new int[n + 1][n + 1];
            set = new HashSet<Integer>();
            q = new LinkedList<>();

            int count = 0;
            int ceros = 0;
            while (aris-- > 0) {

                st = new StringTokenizer(in.nextLine());
                int cur = Integer.parseInt(st.nextToken());
                if (cur == 0) {
                    count++;
                } else {
                    int aux = Integer.parseInt(st.nextToken()) - 1;

                    if (!set.contains(aux))
                        q.add(aux);
                    set.add(aux);
                    while (st.hasMoreTokens()) {
                        int act = Integer.parseInt(st.nextToken()) - 1;

                        graph[aux][act] = 1;
                        graph[act][aux] = 1;

                        aux = act;
                        if (!set.contains(act))
                            q.add(act);
                        set.add(act);
                    }
                }
            }
            boolean enter = false;

            while (!q.isEmpty()) {
                boolean[] visited = new boolean[n + 1];
                dfs(q.peek(), visited);
                enter = true;
                count++;
            }
            if (enter)
                count--;
            out.println(count);

        }

        void dfs(int nod, boolean[] visited) {
            visited[nod] = true;
            q.poll();
            for (int i = 0; i < graph[nod].length; i++) {
                if (!visited[i] && graph[nod][i] == 1) {
                    dfs(i, visited);
                }
            }

        }

    }

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String nextLine() throws IOException {
            return reader.readLine();
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Set;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.Queue;
import java.io.BufferedReader;
import java.util.LinkedList;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        A170 solver = new A170();
        solver.solve(1, in, out);
        out.close();
    }

    static class A170 {
        int[][] graph;
        Set<Integer> set;
        Queue<Integer> q;

        public void solve(int testNumber, InputReader in, PrintWriter out) throws IOException {

            StringTokenizer st = new StringTokenizer(in.nextLine());
            int aris = Integer.parseInt(st.nextToken());
            int n = Integer.parseInt(st.nextToken());
            graph = new int[n][n];
            set = new HashSet<Integer>();
            q = new LinkedList<>();

            int count = 0;
            while (aris-- > 0) {

                st = new StringTokenizer(in.nextLine());
                int cur = Integer.parseInt(st.nextToken());
                if (cur == 0)
                    count++;
                else {
                    int aux = Integer.parseInt(st.nextToken()) - 1;

                    if (!set.contains(aux))
                        q.add(aux);
                    set.add(aux);
                    while (st.hasMoreTokens()) {
                        int act = Integer.parseInt(st.nextToken()) - 1;
                        graph[aux][act] = 1;
                        graph[act][aux] = 1;

                        aux = act;
                        if (!set.contains(act))
                            q.add(act);
                        set.add(act);
                    }
                }
            }

            while (!q.isEmpty()) {
                boolean[] visited = new boolean[n];
                dfs(q.peek(), visited);

                count++;
            }

            out.println(count - 1);

        }

        void dfs(int nod, boolean[] visited) {
            visited[nod] = true;
            q.poll();
            for (int i = 0; i < graph[nod].length; i++) {
                if (!visited[i] && graph[nod][i] == 1) {
                    dfs(i, visited);
                }
            }

        }

    }

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String nextLine() throws IOException {
            return reader.readLine();
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.StringTokenizer;
import java.io.Writer;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author zodiacLeo
 */
public class Main
{
    public static void main(String[] args)
    {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        FastPrinter out = new FastPrinter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA
    {
        public void solve(int testNumber, FastScanner in, FastPrinter out)
        {
            int n = in.nextInt();
            int m = in.nextInt();

            boolean any = false;
            boolean[][] know = new boolean[n][m];
            for (int i = 0; i < n; i++)
            {
                int from = i;
                int cnt = in.nextInt();
                for (int j = 0; j < cnt; j++)
                {
                    int to = in.nextInt() - 1;
                    know[from][to] = true;
                    any = true;
                }
            }
            if (!any)
            {
                out.println(n);
                return;
            }

            UFDS set = new UFDS(n);
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    for (int k = 0; k < m; k++)
                    {
                        if (know[i][k] && know[j][k])
                        {
                            set.connect(i, j);
                        }
                    }
                }
            }
            int count = set.getConnectedComponents();
            out.println(count - 1);
        }

    }

    static class UFDS
    {
        public int n;
        public int[] rank;
        public int[] parent;

        public UFDS(int n)
        {
            this.n = n;
            this.rank = new int[n];
            this.parent = new int[n];
            for (int i = 0; i < n; i++)
            {
                rank[i] = 1;
                parent[i] = i;
            }
        }

        public int root(int i)
        {
            return parent[i] == i ? i : (parent[i] = root(parent[i]));
        }

        public void connect(int i, int j)
        {
            int X = root(i);
            int Y = root(j);
            if (X != Y)
            {
                if (rank[X] < rank[Y])
                {
                    parent[Y] = X;
                } else
                {
                    parent[X] = Y;
                    if (rank[X] == rank[Y])
                    {
                        rank[Y]++;
                    }
                }
            }
        }

        public int getConnectedComponents()
        {
            int count = 0;
            for (int i = 0; i < n; i++)
            {
                if (i == root(i))
                {
                    count++;
                }
            }
            return count;
        }

    }

    static class FastScanner
    {
        public BufferedReader br;
        public StringTokenizer st;

        public FastScanner(InputStream is)
        {
            br = new BufferedReader(new InputStreamReader(is));
        }

        public FastScanner(File f)
        {
            try
            {
                br = new BufferedReader(new FileReader(f));
            } catch (FileNotFoundException e)
            {
                e.printStackTrace();
            }
        }

        public String next()
        {
            while (st == null || !st.hasMoreElements())
            {
                String s = null;
                try
                {
                    s = br.readLine();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
                if (s == null)
                    return null;
                st = new StringTokenizer(s);
            }
            return st.nextToken();
        }

        public int nextInt()
        {
            return Integer.parseInt(next());
        }

    }

    static class FastPrinter extends PrintWriter
    {
        public FastPrinter(OutputStream out)
        {
            super(out);
        }

        public FastPrinter(Writer out)
        {
            super(out);
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.io.Writer;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author zodiacLeo
 */
public class Main
{
    public static void main(String[] args)
    {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        FastPrinter out = new FastPrinter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA
    {
        private int V;
        private boolean[] was;
        private ArrayList[] g;

        public void solve(int testNumber, FastScanner in, FastPrinter out)
        {
            int P = in.nextInt();
            int L = in.nextInt();
            V = P + L;
            g = createGraph(V);
            for (int i = 0; i < P; i++)
            {
                int from = i;
                int n = in.nextInt();
                for (int j = 0; j < n; j++)
                {
                    int lang = in.nextInt() - 1;
                    int to = P + lang;
                    addEdge(from, to);
                }
            }
            int count = 0;
            was = new boolean[V];
            for (int i = 0; i < P; i++)
            {
                if (!was[i])
                {
                    dfs(i);
                    count++;
                }
            }
            out.println(count - 1);
        }

        private void dfs(int u)
        {
            was[u] = true;
            ArrayList<Integer> next = g[u];
            for (Integer v : next)
            {
                if (!was[v])
                {
                    dfs(v);
                }
            }
        }

        private ArrayList[] createGraph(int V)
        {
            ArrayList[] g = new ArrayList[V];
            for (int i = 0; i < V; i++)
            {
                g[i] = new ArrayList<Integer>();
            }
            return g;
        }

        private void addEdge(int from, int to)
        {
            g[from].add(to);
            g[to].add(from);
        }

    }

    static class FastPrinter extends PrintWriter
    {
        public FastPrinter(OutputStream out)
        {
            super(out);
        }

        public FastPrinter(Writer out)
        {
            super(out);
        }

    }

    static class FastScanner
    {
        public BufferedReader br;
        public StringTokenizer st;

        public FastScanner(InputStream is)
        {
            br = new BufferedReader(new InputStreamReader(is));
        }

        public FastScanner(File f)
        {
            try
            {
                br = new BufferedReader(new FileReader(f));
            } catch (FileNotFoundException e)
            {
                e.printStackTrace();
            }
        }

        public String next()
        {
            while (st == null || !st.hasMoreElements())
            {
                String s = null;
                try
                {
                    s = br.readLine();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
                if (s == null)
                    return null;
                st = new StringTokenizer(s);
            }
            return st.nextToken();
        }

        public int nextInt()
        {
            return Integer.parseInt(next());
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.TreeMap;
import java.util.Scanner;
import java.util.ArrayList;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author zodiacLeo
 */
public class Main
{
    public static void main(String[] args)
    {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        Scanner in = new Scanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA
    {
        private int n;
        private int m;
        private boolean mark[];
        private TreeMap<Integer, ArrayList<Integer>> langPersonMapping;

        public void solve(int testNumber, Scanner in, PrintWriter out)
        {
            n = in.nextInt();
            m = in.nextInt();
            mark = new boolean[m];
            langPersonMapping = new TreeMap<Integer, ArrayList<Integer>>();
            for (int i = 0; i < m; i++)
            {
                int lang = i;
                ArrayList<Integer> persons = new ArrayList<Integer>();
                int p = in.nextInt();
                while (p-- > 0)
                {
                    persons.add(in.nextInt());
                }
                langPersonMapping.put(lang, persons);
            }

            int result = 0;
            for (int i = 0; i < m; i++)
            {
                if (!mark[i])
                {
                    dfs(i);
                    result++;
                }
            }
            out.println(result - 1);
        }

        public void dfs(int langID)
        {
            if (mark[langID])
            {
                return;
            }
            mark[langID] = true;
            ArrayList<Integer> langOnePersons = langPersonMapping.get(langID);
            for (int i = 0; i < m; i++)
            {
                ArrayList<Integer> langTwoPerson = langPersonMapping.get(i);
                if (i != langID && !mark[i])
                {
                    for (int j = 0; j < langOnePersons.size(); j++)
                    {
                        if (langTwoPerson.contains(langOnePersons.get(j)))
                        {
                            dfs(i);
                        }
                    }
                }
            }
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.StringTokenizer;
import java.io.Writer;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author zodiacLeo
 */
public class Main
{
    public static void main(String[] args)
    {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        FastPrinter out = new FastPrinter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA
    {
        public void solve(int testNumber, FastScanner in, FastPrinter out)
        {
            int n = in.nextInt();
            int m = in.nextInt();
            boolean any = false;
            boolean[][] know = new boolean[n][m];
            for (int i = 0; i < n; i++)
            {
                int from = i;
                int cnt = in.nextInt();
                for (int j = 0; j < cnt; j++)
                {
                    int to = in.nextInt() - 1;
                    know[from][to] = true;
                    any = true;
                }
            }
            if (!any)
            {
                out.println(n);
                return;
            }

            boolean[][] g = new boolean[n][n];
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    for (int k = 0; k < m; k++)
                    {
                        if (know[i][k] && know[j][k])
                        {
                            g[i][j] = true;
                        }
                    }
                }
            }

            for (int k = 0; k < n; k++)
            {
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < n; j++)
                    {
                        g[i][j] |= (g[i][k] && g[k][j]);
                    }
                }
            }

            int res = 0;
            boolean[] mark = new boolean[n];
            for (int i = 0; i < n; i++)
            {
                if (!mark[i])
                {
                    res++;
                    for (int j = 0; j < n; j++)
                    {
                        if (g[i][j])
                        {
                            mark[j] = true;
                        }
                    }
                }
            }
            out.println(res - 1);
        }

    }

    static class FastScanner
    {
        public BufferedReader br;
        public StringTokenizer st;

        public FastScanner(InputStream is)
        {
            br = new BufferedReader(new InputStreamReader(is));
        }

        public FastScanner(File f)
        {
            try
            {
                br = new BufferedReader(new FileReader(f));
            } catch (FileNotFoundException e)
            {
                e.printStackTrace();
            }
        }

        public String next()
        {
            while (st == null || !st.hasMoreElements())
            {
                String s = null;
                try
                {
                    s = br.readLine();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
                if (s == null)
                    return null;
                st = new StringTokenizer(s);
            }
            return st.nextToken();
        }

        public int nextInt()
        {
            return Integer.parseInt(next());
        }

    }

    static class FastPrinter extends PrintWriter
    {
        public FastPrinter(OutputStream out)
        {
            super(out);
        }

        public FastPrinter(Writer out)
        {
            super(out);
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.io.Writer;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author zodiacLeo
 */
public class Main
{
    public static void main(String[] args)
    {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        FastPrinter out = new FastPrinter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA
    {
        private int V;
        private boolean[] was;
        private ArrayList[] g;

        public void solve(int testNumber, FastScanner in, FastPrinter out)
        {
            int P = in.nextInt();
            int L = in.nextInt();
            V = P + L;
            g = createGraph(V);
            int E = 0;
            for (int i = 0; i < P; i++)
            {
                int from = i;
                int n = in.nextInt();
                for (int j = 0; j < n; j++)
                {
                    int lang = in.nextInt() - 1;
                    int to = P + lang;
                    addEdge(from, to);
                    E++;
                }
            }
            if (E == 0)
            {
                out.println(P);
                return;
            }

            int count = 0;
            was = new boolean[V];
            for (int i = 0; i < P; i++)
            {
                if (!was[i])
                {
                    dfs(i);
                    count++;
                }
            }
            out.println(count - 1);
        }

        private void dfs(int u)
        {
            was[u] = true;
            ArrayList<Integer> next = g[u];
            for (Integer v : next)
            {
                if (!was[v])
                {
                    dfs(v);
                }
            }
        }

        private ArrayList[] createGraph(int V)
        {
            ArrayList[] g = new ArrayList[V];
            for (int i = 0; i < V; i++)
            {
                g[i] = new ArrayList<Integer>();
            }
            return g;
        }

        private void addEdge(int from, int to)
        {
            g[from].add(to);
            g[to].add(from);
        }

    }

    static class FastPrinter extends PrintWriter
    {
        public FastPrinter(OutputStream out)
        {
            super(out);
        }

        public FastPrinter(Writer out)
        {
            super(out);
        }

    }

    static class FastScanner
    {
        public BufferedReader br;
        public StringTokenizer st;

        public FastScanner(InputStream is)
        {
            br = new BufferedReader(new InputStreamReader(is));
        }

        public FastScanner(File f)
        {
            try
            {
                br = new BufferedReader(new FileReader(f));
            } catch (FileNotFoundException e)
            {
                e.printStackTrace();
            }
        }

        public String next()
        {
            while (st == null || !st.hasMoreElements())
            {
                String s = null;
                try
                {
                    s = br.readLine();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
                if (s == null)
                    return null;
                st = new StringTokenizer(s);
            }
            return st.nextToken();
        }

        public int nextInt()
        {
            return Integer.parseInt(next());
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.StringTokenizer;
import java.io.Writer;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author zodiacLeo
 */
public class Main
{
    public static void main(String[] args)
    {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        FastPrinter out = new FastPrinter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA
    {
        public void solve(int testNumber, FastScanner in, FastPrinter out)
        {
            int n = in.nextInt();
            int m = in.nextInt();
            boolean anyEdge = false;
            boolean[][] know = new boolean[n][m];
            for (int i = 0; i < n; i++)
            {
                int from = i;
                int cnt = in.nextInt();
                for (int j = 0; j < cnt; j++)
                {
                    int to = in.nextInt() - 1;
                    know[from][to] = true;
                    anyEdge = true;
                }
            }

            if (!anyEdge)
            {
                out.println(n);
                return;
            }
            boolean[][] g = new boolean[n][n];
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    for (int k = 0; k < m; k++)
                    {
                        if (know[i][k] && know[j][k])
                        {
                            g[i][j] = true;
                        }
                    }
                }
            }
            for (int k = 0; k < n; k++)
            {
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < n; j++)
                    {
                        g[i][j] |= (g[i][k] & g[k][j]);
                    }
                }
            }

            int result = 0;
            boolean[] mark = new boolean[n];
            for (int i = 0; i < n; i++)
            {
                if (!mark[i])
                {
                    result++;
                    for (int j = 0; j < n; j++)
                    {
                        if (g[i][j])
                        {
                            mark[j] = true;
                        }
                    }
                }
            }
            out.println(result - 1);

        }

    }

    static class FastScanner
    {
        public BufferedReader br;
        public StringTokenizer st;

        public FastScanner(InputStream is)
        {
            br = new BufferedReader(new InputStreamReader(is));
        }

        public FastScanner(File f)
        {
            try
            {
                br = new BufferedReader(new FileReader(f));
            } catch (FileNotFoundException e)
            {
                e.printStackTrace();
            }
        }

        public String next()
        {
            while (st == null || !st.hasMoreElements())
            {
                String s = null;
                try
                {
                    s = br.readLine();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
                if (s == null)
                    return null;
                st = new StringTokenizer(s);
            }
            return st.nextToken();
        }

        public int nextInt()
        {
            return Integer.parseInt(next());
        }

    }

    static class FastPrinter extends PrintWriter
    {
        public FastPrinter(OutputStream out)
        {
            super(out);
        }

        public FastPrinter(Writer out)
        {
            super(out);
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.TreeMap;
import java.util.Scanner;
import java.util.ArrayList;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author zodiacLeo
 */
public class Main
{
    public static void main(String[] args)
    {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        Scanner in = new Scanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA
    {
        private int n;
        private int m;
        private boolean mark[];
        private TreeMap<Integer, ArrayList<Integer>> langPersonMapping;

        public void solve(int testNumber, Scanner in, PrintWriter out)
        {
            n = in.nextInt();
            m = in.nextInt();
            mark = new boolean[m];
            langPersonMapping = new TreeMap<Integer, ArrayList<Integer>>();
            int already = 0;
            for (int i = 0; i < m; i++)
            {
                int lang = i;
                ArrayList<Integer> persons = new ArrayList<Integer>();
                int p = in.nextInt();
                already += p;
                while (p-- > 0)
                {
                    persons.add(in.nextInt());
                }
                langPersonMapping.put(lang, persons);
            }
            if (already == 0)
            {
                out.println(n);
                return;
            }

            int result = 0;
            for (int i = 0; i < m; i++)
            {
                if (!mark[i])
                {
                    dfs(i);
                    result++;
                }
            }
            out.println(result - 1);
        }

        public void dfs(int langID)
        {
            if (mark[langID])
            {
                return;
            }
            mark[langID] = true;
            ArrayList<Integer> langOnePersons = langPersonMapping.get(langID);
            for (int i = 0; i < m; i++)
            {
                ArrayList<Integer> langTwoPerson = langPersonMapping.get(i);
                if (i != langID && !mark[i])
                {
                    for (int j = 0; j < langOnePersons.size(); j++)
                    {
                        if (langTwoPerson.contains(langOnePersons.get(j)))
                        {
                            dfs(i);
                        }
                    }
                }
            }
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.TreeMap;
import java.util.Scanner;
import java.util.ArrayList;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author zodiacLeo
 */
public class Main
{
    public static void main(String[] args)
    {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        Scanner in = new Scanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA
    {
        private int n;
        private int m;
        private boolean mark;
        private TreeMap<Integer, ArrayList<Integer>> langPersonMapping;

        public void solve(int testNumber, Scanner in, PrintWriter out)
        {
            n = in.nextInt();
            m = in.nextInt();
            mark = new boolean[m];
            langPersonMapping = new TreeMap<Integer, ArrayList<Integer>>();
            int already = 0;
            for (int i = 0; i < m; i++)
            {
                int lang = i;
                ArrayList<Integer> persons = new ArrayList<Integer>();
                int p = in.nextInt();
                already += p;
                while (p-- > 0)
                {
                    persons.add(in.nextInt());
                }
                langPersonMapping.put(lang, persons);
            }
            if (already == 0)
            {
                out.println(n);
                return;
            }

            int result = 0;
            for (int i = 0; i < m; i++)
            {
                if (!mark[i])
                {
                    dfs(i);
                    result++;
                }
            }
            out.println(result - 1);
        }

        public void dfs(int langID)
        {
            if (mark[langID])
            {
                return;
            }
            mark[langID] = true;
            ArrayList<Integer> langOnePersons = langPersonMapping.get(langID);
            for (int i = 0; i < m; i++)
            {
                ArrayList<Integer> langTwoPerson = langPersonMapping.get(i);
                if (i != langID && !mark[i])
                {
                    for (int j = 0; j < langOnePersons.size(); j++)
                    {
                        if (langTwoPerson.contains(langOnePersons.get(j)))
                        {
                            dfs(i);
                        }
                    }
                }
            }
        }

    }
}


import java.util.* ;
import java.io.*;

public class A
{
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        PrintWriter pr = new PrintWriter(System.out);
        int n = sc.nextInt();
        int m = sc.nextInt();
        HashSet<Integer> [] adjSet = new HashSet[n];
        for (int i = 0; i < n ; i++)
            adjSet[i] = new HashSet();
        int numOfSets = 0 ;
        int zeros = 0 ;
        int ans = 0 ;
        int totalSets = 0 ;
        while(n-->0)
        {
            int num = sc.nextInt();
            if(num==0){
                zeros++ ;
                continue ;
            }
            Set<Integer> set = new HashSet<>();
            while(num-->0)
                set.add(sc.nextInt());
            ArrayList<Integer> lst = new ArrayList();
            for (int i = 0; i < numOfSets ; i++) {
                HashSet<Integer> s = new HashSet<>(adjSet[i]);
                s.retainAll(set);
                if(s.size()>0)
                    lst.add(i);
            }
            if(lst.size()>0)
            {
                for (int i = 1 ; i < lst.size() ; i++) {
                    adjSet[lst.get(0)].addAll(adjSet[lst.get(i)]);
                    adjSet[lst.get(i)].clear();
                    totalSets-- ;
                }
                adjSet[lst.get(0)].addAll(set);
            }
            else{
                adjSet[numOfSets++].addAll(set);
                totalSets++ ;
            }
//            System.out.println("totalSets = " + totalSets);
        }
        ans += zeros ;
        if(totalSets>0)
            ans += totalSets-1 ;
        pr.println(ans);
        sc.close();
        pr.close();
    }
}


import java.util.* ;
import java.io.*;
/**
 * @author mostafa
 */
public class A
{
    public static void main(String[] args) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pr = new PrintWriter(System.out);
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        int [] first = new int[m];
        Arrays.fill(first, -1);
        Graph graph = new Graph(n);
        int zeros = 0 ;
        for(int i = 0 ; i < n ; i++)
        {
            st = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(st.nextToken());
            if( x == 0 )  zeros++ ;
            while(x-->0)
            {
                int u = Integer.parseInt(st.nextToken())-1;
                if(first[u]!=-1)  graph.addEdge(first[u], i);
                else              first[u] = i ;
            }
        }
        int count = new CC(graph).count();
        if(count==zeros)  pr.println(count);
        else              pr.println(count-1);
        br.close();
        pr.close();
    }
}
class Graph
{
    private final int V ;
    private ArrayList<Integer> adjList[] ;
    public Graph(int V)
    {
        this.V = V ;
        adjList = new ArrayList[V];
        for (int i = 0; i < V ; i++)
            adjList[i] = new ArrayList();
    }
    public int V()
    {   return V ;   }
    public void addEdge(int u , int v)
    {
        adjList[u].add(v);
        adjList[v].add(u);
    }
    public Iterable<Integer> adj(int v)
    {   return adjList[v];     }
}

class CC
{
    private boolean [] marked ;
    private int count ;
    public CC(Graph G)
    {
        marked = new boolean [G.V()];
        for (int v = 0; v < G.V() ; v++)
            if(!marked[v])
            {
                dfs(G, v);
                count++ ;
            }
    }
    private void dfs(Graph G , int v)
    {
        marked[v] = true ;
        for(int w : G.adj(v))
            if(!marked[w])
                dfs(G, w);
    }
    public int count()
    {   return count ;   }
}


import java.util.* ;
import java.io.*;
/**
 * @author mostafa
 */
public class A
{
    public static void main(String[] args) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pr = new PrintWriter(System.out);
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        int [] first = new int[m];
        Arrays.fill(first, -1);
        Graph graph = new Graph(n);
        for(int i = 0 ; i < n ; i++)
        {
            st = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(st.nextToken());
            while(x-->0)
            {
                int u = Integer.parseInt(st.nextToken())-1;
                if(first[u]!=-1)  graph.addEdge(first[u], i);
                else              first[u] = i ;
            }
        }
        pr.println(new CC(graph).count()-1);
        br.close();
        pr.close();
    }
}
class Graph
{
    private final int V ;
    private ArrayList<Integer> adjList[] ;
    public Graph(int V)
    {
        this.V = V ;
        adjList = new ArrayList[V];
        for (int i = 0; i < V ; i++)
            adjList[i] = new ArrayList();
    }
    public int V()
    {   return V ;   }
    public void addEdge(int u , int v)
    {
        adjList[u].add(v);
        adjList[v].add(u);
    }
    public Iterable<Integer> adj(int v)
    {   return adjList[v];     }
}

class CC
{
    private boolean [] marked ;
    private int count ;
    public CC(Graph G)
    {
        marked = new boolean [G.V()];
        for (int v = 0; v < G.V() ; v++)
            if(!marked[v])
            {
                dfs(G, v);
                count++ ;
            }
    }
    private void dfs(Graph G , int v)
    {
        marked[v] = true ;
        for(int w : G.adj(v))
            if(!marked[w])
                dfs(G, w);
    }
    public int count()
    {   return count ;   }
}


import java.util.* ;
import java.io.*;

public class A
{
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        PrintWriter pr = new PrintWriter(System.out);
        int n = sc.nextInt();
        int m = sc.nextInt();
        HashSet<Integer> [] adjSet = new HashSet[n];
        for (int i = 0; i < n ; i++)
            adjSet[i] = new HashSet();
        int numOfSets = 0 ;
        int zeros = 0 ;
        int totalSets = 0 ;
        while(n-->0)
        {
            int num = sc.nextInt();
            if(num==0){
                zeros++ ;
                continue ;
            }
            Set<Integer> set = new HashSet<>();
            while(num-->0)
                set.add(sc.nextInt());
            ArrayList<Integer> lst = new ArrayList();
            for (int i = 0; i < numOfSets ; i++) {
                HashSet<Integer> s = new HashSet<>(adjSet[i]);
                s.retainAll(set);
                if(s.size()>0)
                    lst.add(i);
            }
            if(lst.size()>0)
            {
                for (int i = 1 ; i < lst.size() ; i++) {
                    adjSet[lst.get(0)].addAll(adjSet[lst.get(i)]);
                    adjSet[lst.get(i)].clear();
                    totalSets-- ;
                }
                adjSet[lst.get(0)].addAll(set);
            }
            else{
                adjSet[numOfSets++].addAll(set);
                totalSets++ ;
            }
//            System.out.println("totalSets = " + totalSets);
        }
        pr.println(totalSets-1+zeros);
        sc.close();
        pr.close();
    }
}


import java.util.* ;
import java.io.*;

public class A
{
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        PrintWriter pr = new PrintWriter(System.out);
        int n = sc.nextInt();
        int m = sc.nextInt();
        HashSet<Integer> [] adjSet = new HashSet[n];
        for (int i = 0; i < n ; i++)
            adjSet[i] = new HashSet();
        int numOfSets = 0 ;
        boolean zeros = false ;
        int ans = 0 ;
        int totalSets = 0 ;
        while(n-->0)
        {
            int num = sc.nextInt();
            if(num==0){
                zeros = true ;
                continue ;
            }
            Set<Integer> set = new HashSet<>();
            while(num-->0)
                set.add(sc.nextInt());
            ArrayList<Integer> lst = new ArrayList();
            for (int i = 0; i < numOfSets ; i++) {
                HashSet<Integer> s = new HashSet<>(adjSet[i]);
                s.retainAll(set);
                if(s.size()>0)
                    lst.add(i);
            }
            if(lst.size()>0)
            {
                for (int i = 1 ; i < lst.size() ; i++) {
                    adjSet[lst.get(0)].addAll(adjSet[lst.get(i)]);
                    adjSet[lst.get(i)].clear();
                    totalSets-- ;
                }
                adjSet[lst.get(0)].addAll(set);
            }
            else{
                adjSet[numOfSets++].addAll(set);
                totalSets++ ;
            }
//            System.out.println("totalSets = " + totalSets);
        }
        if(zeros)
            ans++ ;
        if(totalSets>0)
            ans += totalSets-1 ;
        pr.println(ans);
        sc.close();
        pr.close();
    }
}


import java.util.ArrayList;
import java.util.Scanner;

public class LearningLanguages {
	int[] parents;

	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		LearningLanguages obj = new LearningLanguages();
		int n = input.nextInt();
		int m = input.nextInt();
		obj.parents = new int[m + 1];
		int temp, numOfZero = 0, numOfSets;
		ArrayList<Integer>[] data = new ArrayList[n];
		for (int i = 0; i < n; i++) {
			temp = input.nextInt();
			data[i] = new ArrayList<Integer>();
			if (temp == 0) {
				numOfZero++;
				data[i].add(0);
			} else {
				for (int j = 0; j < temp; j++) {
					data[i].add(input.nextInt());
				}
				obj.control(data[i]);
			}
		}
		numOfSets = obj.solve();
		System.out.println(numOfSets + numOfZero - 1);

	}

	private void control(ArrayList<Integer> employee) {
		int parent = employee.get(0);
		if (employee.size() == 1) {
             if (parents[parent]==0){
            	 parents[parent]=parent;
             }
		} else {
			for (int i = 1; i < employee.size(); i++) {
				union(parent, employee.get(i));
			}
		}
	}

	private int getRoot(int x) {
		if (parents[x] == 0) {
			parents[x] = x;
		}
		return parents[x];
	}

	private void union(int x, int y) {
		int parentX = getRoot(x);
		int parentY = getRoot(y);
		if (parentY != parentX) {
			for (int i = 0; i < parents.length; i++) {
				if (parents[i] == parentY) {
					parents[i] = parentX;
				}
			}
		}

	}

	private int solve() {
		int ans = 0;
		boolean visited[] = new boolean[parents.length];
		for (int i = 1; i < parents.length; i++) {
			visited[parents[i]] = true;
		}
		for (int i = 1; i < parents.length; i++) {
			if (visited[i]) {
				ans++;
			}
		}
		return ans;
	}

}


import java.util.ArrayList;
import java.util.Scanner;
//
public class LearningLanguages {
	int[] parents;

	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		LearningLanguages obj = new LearningLanguages();
		int n = input.nextInt();
		int m = input.nextInt();
		obj.parents = new int[m + 1];
		int temp, numOfZero = 0, numOfSets;
		ArrayList<Integer>[] data = new ArrayList[n];
		for (int i = 0; i < n; i++) {
			temp = input.nextInt();
			data[i] = new ArrayList<Integer>();
			if (temp == 0) {
				numOfZero++;
				data[i].add(0);
			} else {
				for (int j = 0; j < temp; j++) {
					data[i].add(input.nextInt());
				}
				obj.control(data[i]);
			}
		}
		numOfSets = obj.solve();
		System.out.println(numOfSets + numOfZero);

	}

	private void control(ArrayList<Integer> employee) {
		int parent = employee.get(0);
		if (employee.size() == 1) {
             if (parents[parent]==0){
            	 parents[parent]=parent;
             }
		} else {
			for (int i = 1; i < employee.size(); i++) {
				union(parent, employee.get(i));
			}
		}
	}

	private int getRoot(int x) {
		if (parents[x] == 0) {
			parents[x] = x;
		}
		return parents[x];
	}

	private void union(int x, int y) {
		int parentX = getRoot(x);
		int parentY = getRoot(y);
		if (parentY != parentX) {
			for (int i = 0; i < parents.length; i++) {
				if (parents[i] == parentY) {
					parents[i] = parentX;
				}
			}
		}

	}
//
	private int solve() {
		int ans = 0;
		boolean visited[] = new boolean[parents.length];
		for (int i = 1; i < parents.length; i++) {
			visited[parents[i]] = true;
		}
		for (int i = 1; i < parents.length; i++) {
			if (visited[i]) {
				ans++;
			}
		}
		if (ans>0){
			ans--;
		}
		return ans;
	}

}


import java.util.ArrayList;
import java.util.Scanner;

public class LearningLanguages {
	private int[] languages;
	private int marker = 1, result = 0;

	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		LearningLanguages obj = new LearningLanguages();
		int n, m, k,numOfSets;
		n = input.nextInt();
		m = input.nextInt();
		obj.languages = new int[m + 1];
		ArrayList<Integer>[] data = new ArrayList[n];
		for (int i = 0; i < n; i++) {
			k = input.nextInt();
			data[i] = new ArrayList<Integer>();
			for (int j = 0; j < k; j++) {
				data[i].add(input.nextInt());
			}
			if (k==0){
				data[i].add(0);
			}
		}
		for (int i = 0; i < n; i++) {
			if (data[i].get(0) == 0) {
				obj.result++;
			} else {
				obj.solve(data[i]);
			}
		}
		numOfSets=obj.setsNum();
		obj.result+=numOfSets-1;
		System.out.println(obj.result);

	}

	private void solve(ArrayList<Integer> employee) {
		ArrayList<Integer> connected = new ArrayList<Integer>();
		boolean newSet=true;
		int min=101,temp;
		for (int i = 0; i < employee.size(); i++) {
			if (languages[employee.get(i)] == 0) {
				languages[employee.get(i)] = marker;
			} else {
				newSet=false;
				if (connected.size() == 0 || !connected.contains(languages[employee.get(i)]))
					connected.add(languages[employee.get(i)]);
				    temp=languages[employee.get(i)];
				    if (temp<min){
				    	min=temp;
				    }
			}
		}
		if (newSet){
			marker++;
		}
		else if (connected.size()==1){
			for (int i=0;i<employee.size();i++){
				languages[employee.get(i)]=min;
			}
		}else{
			for (int i=0;i<languages.length;i++){
				if (connected.contains(languages[i])){
					languages[i]=min;
				}
			}
			marker=min+1;
		}
	}
	private int setsNum(){
		int num=0;
		for (int i=0;i<languages.length;i++){
			if (languages[i]>num){
				num=languages[i];
			}
		}
		return num;
	}

}


import java.util.ArrayList;
import java.util.Scanner;

public class LearningLanguages {
	int[] parents;

	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		LearningLanguages obj = new LearningLanguages();
		int n = input.nextInt();
		int m = input.nextInt();
		obj.parents = new int[m + 1];
		int temp, numOfZero = 0, numOfSets;
		ArrayList<Integer>[] data = new ArrayList[n];
		for (int i = 0; i < n; i++) {
			temp = input.nextInt();
			data[i] = new ArrayList<Integer>();
			if (temp == 0) {
				numOfZero++;
				data[i].add(0);
			} else {
				for (int j = 0; j < temp; j++) {
					data[i].add(input.nextInt());
				}
				obj.control(data[i]);
			}
		}
		numOfSets = obj.solve();
		System.out.println(numOfSets + numOfZero);

	}

	private void control(ArrayList<Integer> employee) {
		int parent = employee.get(0);
		if (employee.size() == 1) {
             if (parents[parent]==0){
            	 parents[parent]=parent;
             }
		} else {
			for (int i = 1; i < employee.size(); i++) {
				union(parent, employee.get(i));
			}
		}
	}

	private int getRoot(int x) {
		if (parents[x] == 0) {
			parents[x] = x;
		}
		return parents[x];
	}

	private void union(int x, int y) {
		int parentX = getRoot(x);
		int parentY = getRoot(y);
		if (parentY != parentX) {
			for (int i = 0; i < parents.length; i++) {
				if (parents[i] == parentY) {
					parents[i] = parentX;
				}
			}
		}

	}

	private int solve() {
		int ans = 0;
		boolean visited[] = new boolean[parents.length];
		for (int i = 1; i < parents.length; i++) {
			visited[parents[i]] = true;
		}
		for (int i = 1; i < parents.length; i++) {
			if (visited[i]) {
				ans++;
			}
		}
		if (ans>0){
			ans--;
		}
		return ans;
	}

}


import java.util.ArrayList;
import java.util.Scanner;

public class LearningLanguages {
	int[] parents;

	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		LearningLanguages obj = new LearningLanguages();
		int n = input.nextInt();
		int m = input.nextInt();
		obj.parents = new int[m + 1];
		int temp, numOfZero = 0, numOfSets;
		ArrayList<Integer>[] data = new ArrayList[n];
		for (int i = 0; i < n; i++) {
			temp = input.nextInt();
			data[i] = new ArrayList<Integer>();
			if (temp == 0) {
				numOfZero++;
				data[i].add(0);
			} else {
				for (int j = 0; j < temp; j++) {
					data[i].add(input.nextInt());
				}
				obj.control(data[i]);
			}
		}
		numOfSets = obj.solve();
		System.out.println(numOfSets + numOfZero);

	}

	private void control(ArrayList<Integer> employee) {
		int parent = employee.get(0);
		if (employee.size() == 1) {
             if (parents[parent]==0){
            	 parents[parent]=parent;
             }
		} else {
			for (int i = 1; i < employee.size(); i++) {
				union(parent, employee.get(i));
			}
		}
	}

	private int getRoot(int x) {
		if (parents[x] == 0) {
			parents[x] = x;
		}
		return parents[x];
	}

	private void union(int x, int y) {
		int parentX = getRoot(x);
		int parentY = getRoot(y);
		if (parentY != parentX) {
			for (int i = 0; i < parents.length; i++) {
				if (parents[i] == parentY) {
					parents[i] = parentX;
				}
			}
		}

	}
//
	private int solve() {
		int ans = 0;
		boolean visited[] = new boolean[parents.length];
		for (int i = 1; i < parents.length; i++) {
			visited[parents[i]] = true;
		}
		for (int i = 1; i < parents.length; i++) {
			if (visited[i]) {
				ans++;
			}
		}
		if (ans>0){
			ans--;
		}
		return ans;
	}

}


import java.io.*;
import java.util.*;
public class LearningLanguages {
	static int n, m;
	static int[] parent;
	static int find(int x) {
		return (x == parent[x] ? (x) : (parent[x] = find(parent[x])));
	}
	static void union(int a, int b) {
		int p1 = find(a);
		int p2 = find(b);
		if(p1 != p2) {
			parent[p1] = p2;
		}
	}
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		n = Integer.parseInt(st.nextToken()); m = Integer.parseInt(st.nextToken());
		parent = new int[m+1];
		HashSet<Integer> h = new HashSet<Integer>();
		for(int i = 1; i <= m; i++) parent[i] = i;
		for(int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine());
			int k = Integer.parseInt(st.nextToken());
			if(k == 0) parent[0]++;
			else if(k == 1) {
				int first = Integer.parseInt(st.nextToken());
				h.add(first);
			}
			else if(k > 1) {
				int first = Integer.parseInt(st.nextToken());
				h.add(first);
				for(int j = 1; j < k; j++) {
					int next = Integer.parseInt(st.nextToken());
					union(first, next);
					h.add(next);
				}
			}
		}
		//System.out.println(Arrays.toString(parent));
		int ret = parent[0];
		HashSet<Integer> hset = new HashSet<Integer>();
		for(int i = 1; i <= m; i++) {
			int par = find(i);
			//System.out.println(i + " " + par);
			if(!hset.contains(par) && h.contains(par)) {
				//System.out.println(i);
				hset.add(par);
				ret++;
			}
		}
		System.out.println(ret-1);
	}
}


import java.io.*;
import java.util.*;
public class LearningLanguages {
	static int n, m;
	static int[] parent;
	static int find(int x) {
		return (x == parent[x] ? (x) : (parent[x] = find(parent[x])));
	}
	static void union(int a, int b) {
		int p1 = find(a);
		int p2 = find(b);
		if(p1 != p2) {
			parent[p1] = p2;
		}
	}
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		n = Integer.parseInt(st.nextToken()); m = Integer.parseInt(st.nextToken());
		parent = new int[m+1];
		HashSet<Integer> h = new HashSet<Integer>();
		for(int i = 1; i <= m; i++) parent[i] = i;
		for(int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine());
			int k = Integer.parseInt(st.nextToken());
			if(k == 0) parent[0]++;
			else if(k == 1) {
				int first = Integer.parseInt(st.nextToken());
				h.add(first);
			}
			else if(k > 1) {
				int first = Integer.parseInt(st.nextToken());
				h.add(first);
				for(int j = 1; j < k; j++) {
					int next = Integer.parseInt(st.nextToken());
					union(first, next);
					h.add(next);
				}
			}
		}
		//System.out.println(Arrays.toString(parent));
		int ret = parent[0];
		HashSet<Integer> hset = new HashSet<Integer>();
		for(int i = 1; i <= m; i++) {
			int par = find(i);
			//System.out.println(i + " " + par);
			if(!hset.contains(par) && h.contains(par)) {
				//System.out.println(i);
				if(i == 1) ret--;
				hset.add(par);
				ret++;
			}
		}
		System.out.println(ret);
	}
}


import java.io.*;
import java.util.*;
public class LearningLanguages {
	static int n, m;
	static int[] parent;
	static int find(int x) {
		return (x == parent[x] ? (x) : (parent[x] = find(parent[x])));
	}
	static void union(int a, int b) {
		int p1 = find(a);
		int p2 = find(b);
		if(p1 != p2) {
			parent[p1] = p2;
		}
	}
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		n = Integer.parseInt(st.nextToken()); m = Integer.parseInt(st.nextToken());
		parent = new int[m+1];
		HashSet<Integer> h = new HashSet<Integer>();
		for(int i = 1; i <= m; i++) parent[i] = i;
		for(int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine());
			int k = Integer.parseInt(st.nextToken());
			if(k == 0) parent[0]++;
			else if(k == 1) {
				int first = Integer.parseInt(st.nextToken());
				h.add(first);
			}
			else if(k > 1) {
				int first = Integer.parseInt(st.nextToken());
				h.add(first);
				for(int j = 1; j < k; j++) {
					int next = Integer.parseInt(st.nextToken());
					union(first, next);
					h.add(next);
				}
			}
		}
		//System.out.println(Arrays.toString(parent));
		int ret = parent[0];
		HashSet<Integer> hset = new HashSet<Integer>();
		boolean b = true;
		//System.out.println(Arrays.toString(parent));
		for(int i = 1; i <= m; i++) {
			int par = find(i);
			//System.out.println(i + " " + par);
			if(!hset.contains(par) && h.contains(par)) {
				//System.out.println(i);
				if(b) {
					ret--;
					b = false;
				}

				hset.add(par);
				ret++;
			}
		}
		System.out.println(ret);
	}
}


import java.io.*;
import java.util.*;
public class LearningLanguages {
	static int n, m;
	static int[] parent;
	static int find(int x) {
		return (x == parent[x] ? (x) : (parent[x] = find(parent[x])));
	}
	static void union(int a, int b) {
		int p1 = find(a);
		int p2 = find(b);
		if(p1 != p2) {
			parent[p1] = p2;
		}
	}
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		n = Integer.parseInt(st.nextToken()); m = Integer.parseInt(st.nextToken());
		parent = new int[m+1];
		HashSet<Integer> h = new HashSet<Integer>();
		for(int i = 1; i <= m; i++) parent[i] = i;
		for(int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine());
			int k = Integer.parseInt(st.nextToken());
			if(k == 0) parent[0]++;
			else if(k == 1) {
				int first = Integer.parseInt(st.nextToken());
				h.add(first);
			}
			else if(k > 1) {
				int first = Integer.parseInt(st.nextToken());
				h.add(first);
				for(int j = 1; j < k; j++) {
					int next = Integer.parseInt(st.nextToken());
					union(first, next);
					h.add(next);
				}
			}
		}
		//System.out.println(Arrays.toString(parent));
		int ret = parent[0];
		HashSet<Integer> hset = new HashSet<Integer>();
		for(int i = 1; i <= m; i++) {
			if(i == 1) ret--;
			int par = find(i);
			//System.out.println(i + " " + par);
			if(!hset.contains(par) && h.contains(par)) {
				//System.out.println(i);
				hset.add(par);
				ret++;
			}
		}
		System.out.println(ret);
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Test1 {
	static ArrayList<Integer>[] adjList;
	static boolean[] visited;
	static boolean yes;

	static void dfs(int a) {
		visited[a] = true;
		for (int x : adjList[a])
			if (!visited[x]) {
				dfs(x);
				yes = true;
			}
	}

	public static void main(String[] args) throws IOException {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		adjList = new ArrayList[100001];
		for (int i = 0; i < adjList.length; i++) {
			adjList[i] = new ArrayList<Integer>();
		}
		HashSet<Integer> hs = new HashSet<Integer>();
		boolean f = false;
		for (int i = 1; i <= n; i++) {
			int k = sc.nextInt();
			hs.add(i*1001);
			int[] tmp = new int[k];
			if(k>0)
				f = true;
			for (int j = 0; j < k; j++) {
				tmp[j] = sc.nextInt() ;
			}
			for (int j = 0; j < k; j++) {
				adjList[i * 1001].add(tmp[j]);
				adjList[tmp[j]].add(i * 1001);
			}
		}
		visited = new boolean[adjList.length];
		int c = 0;
		for (int i = 0; i <visited.length ; i++) {
			if (!visited[i] && hs.contains(i)) {
				dfs(i);
				c++;
			}
		}
		System.out.println(f?c - 1:c);
	}

	static class Scanner {
		StringTokenizer st;
		BufferedReader br;

		public Scanner(InputStream s) {
			br = new BufferedReader(new InputStreamReader(s));
		}

		public String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}

		public int nextInt() throws IOException {
			return Integer.parseInt(next());
		}

		public long nextLong() throws IOException {
			return Long.parseLong(next());
		}

		public String nextLine() throws IOException {
			return br.readLine();
		}

		public double nextDouble() throws IOException {
			String x = next();
			StringBuilder sb = new StringBuilder("0");
			double res = 0, f = 1;
			boolean dec = false, neg = false;
			int start = 0;
			if (x.charAt(0) == '-') {
				neg = true;
				start++;
			}
			for (int i = start; i < x.length(); i++)
				if (x.charAt(i) == '.') {
					res = Long.parseLong(sb.toString());
					sb = new StringBuilder("0");
					dec = true;
				} else {
					sb.append(x.charAt(i));
					if (dec)
						f *= 10;
				}
			res += Long.parseLong(sb.toString()) / f;
			return res * (neg ? -1 : 1);
		}

		public boolean ready() throws IOException {
			return br.ready();
		}

	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Test1 {
	static ArrayList<Integer>[] adjList;
	static boolean[] visited;
	static boolean yes;

	static void dfs(int a) {
		visited[a] = true;
		for (int x : adjList[a])
			if (!visited[x]) {
				dfs(x);
				yes = true;
			}
	}

	public static void main(String[] args) throws IOException {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		adjList = new ArrayList[100001];
		for (int i = 0; i < 100001; i++) {
			adjList[i] = new ArrayList<Integer>();
		}
		HashSet<Integer> hs = new HashSet<Integer>();
		for (int i = 1; i <= n; i++) {
			int k = sc.nextInt();
			hs.add(i*1000);
			int[] tmp = new int[k];
			for (int j = 0; j < k; j++) {
				tmp[j] = sc.nextInt() ;
			}
			for (int j = 0; j < k; j++) {
				adjList[i * 1000].add(tmp[j]);
				adjList[tmp[j]].add(i * 1000);
			}
		}
		visited = new boolean[100000];
		int c = 0;
		for (int i = 0; i <100000 ; i++) {
			if (!visited[i] && hs.contains(i)) {
				dfs(i);
				c++;
			}
		}
		System.out.println(c - 1);
	}

	static class Scanner {
		StringTokenizer st;
		BufferedReader br;

		public Scanner(InputStream s) {
			br = new BufferedReader(new InputStreamReader(s));
		}

		public String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}

		public int nextInt() throws IOException {
			return Integer.parseInt(next());
		}

		public long nextLong() throws IOException {
			return Long.parseLong(next());
		}

		public String nextLine() throws IOException {
			return br.readLine();
		}

		public double nextDouble() throws IOException {
			String x = next();
			StringBuilder sb = new StringBuilder("0");
			double res = 0, f = 1;
			boolean dec = false, neg = false;
			int start = 0;
			if (x.charAt(0) == '-') {
				neg = true;
				start++;
			}
			for (int i = start; i < x.length(); i++)
				if (x.charAt(i) == '.') {
					res = Long.parseLong(sb.toString());
					sb = new StringBuilder("0");
					dec = true;
				} else {
					sb.append(x.charAt(i));
					if (dec)
						f *= 10;
				}
			res += Long.parseLong(sb.toString()) / f;
			return res * (neg ? -1 : 1);
		}

		public boolean ready() throws IOException {
			return br.ready();
		}

	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Test1 {
	static ArrayList<Integer>[] adjList;
	static boolean[] visited;
	static boolean yes;

	static void dfs(int a) {
		visited[a] = true;
		for (int x : adjList[a])
			if (!visited[x]) {
				dfs(x);
				yes = true;
			}
	}

	public static void main(String[] args) throws IOException {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		adjList = new ArrayList[100001];
		for (int i = 0; i < adjList.length; i++) {
			adjList[i] = new ArrayList<Integer>();
		}
		HashSet<Integer> hs = new HashSet<Integer>();
		boolean f = false;
		for (int i = 1; i <= n; i++) {
			int k = sc.nextInt();

			hs.add(i*1001);
			int[] tmp = new int[k];
			if(k>0)
				f = true;
			for (int j = 0; j < k; j++) {
				tmp[j] = sc.nextInt() ;
			}
			for (int j = 0; j < k; j++) {
				adjList[i * 1001].add(tmp[j]);
				adjList[tmp[j]].add(i * 1001);
			}
		}
		visited = new boolean[adjList.length];
		int c = 0;
		for (int i = 0; i <visited.length ; i++) {
			if (!visited[i] && hs.contains(i)) {
				dfs(i);
				c++;
			}
		}
		System.out.println(f?c - 1:c);
	}

	static class Scanner {
		StringTokenizer st;
		BufferedReader br;

		public Scanner(InputStream s) {
			br = new BufferedReader(new InputStreamReader(s));
		}

		public String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}

		public int nextInt() throws IOException {
			return Integer.parseInt(next());
		}

		public long nextLong() throws IOException {
			return Long.parseLong(next());
		}

		public String nextLine() throws IOException {
			return br.readLine();
		}

		public double nextDouble() throws IOException {
			String x = next();
			StringBuilder sb = new StringBuilder("0");
			double res = 0, f = 1;
			boolean dec = false, neg = false;
			int start = 0;
			if (x.charAt(0) == '-') {
				neg = true;
				start++;
			}
			for (int i = start; i < x.length(); i++)
				if (x.charAt(i) == '.') {
					res = Long.parseLong(sb.toString());
					sb = new StringBuilder("0");
					dec = true;
				} else {
					sb.append(x.charAt(i));
					if (dec)
						f *= 10;
				}
			res += Long.parseLong(sb.toString()) / f;
			return res * (neg ? -1 : 1);
		}

		public boolean ready() throws IOException {
			return br.ready();
		}

	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Test1 {
	static ArrayList<Integer>[] adjList;
	static boolean[] visited;
	static boolean yes;

	static void dfs(int a) {
		visited[a] = true;
		for (int x : adjList[a])
			if (!visited[x]) {
				dfs(x);
				yes = true;
			}
	}

	public static void main(String[] args) throws IOException {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		adjList = new ArrayList[100001];
		for (int i = 0; i < 100001; i++) {
			adjList[i] = new ArrayList<Integer>();
		}
		HashSet<Integer> hs = new HashSet<Integer>();
		boolean f = false;
		for (int i = 1; i <= n; i++) {
			int k = sc.nextInt();
			hs.add(i*1000);
			int[] tmp = new int[k];
			if(k>0)
				f = true;
			for (int j = 0; j < k; j++) {
				tmp[j] = sc.nextInt() ;
			}
			for (int j = 0; j < k; j++) {
				adjList[i * 1000].add(tmp[j]);
				adjList[tmp[j]].add(i * 1000);
			}
		}
		visited = new boolean[100000];
		int c = 0;
		for (int i = 0; i <100000 ; i++) {
			if (!visited[i] && hs.contains(i)) {
				dfs(i);
				c++;
			}
		}
		System.out.println(f?c - 1:c);
	}

	static class Scanner {
		StringTokenizer st;
		BufferedReader br;

		public Scanner(InputStream s) {
			br = new BufferedReader(new InputStreamReader(s));
		}

		public String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}

		public int nextInt() throws IOException {
			return Integer.parseInt(next());
		}

		public long nextLong() throws IOException {
			return Long.parseLong(next());
		}

		public String nextLine() throws IOException {
			return br.readLine();
		}

		public double nextDouble() throws IOException {
			String x = next();
			StringBuilder sb = new StringBuilder("0");
			double res = 0, f = 1;
			boolean dec = false, neg = false;
			int start = 0;
			if (x.charAt(0) == '-') {
				neg = true;
				start++;
			}
			for (int i = start; i < x.length(); i++)
				if (x.charAt(i) == '.') {
					res = Long.parseLong(sb.toString());
					sb = new StringBuilder("0");
					dec = true;
				} else {
					sb.append(x.charAt(i));
					if (dec)
						f *= 10;
				}
			res += Long.parseLong(sb.toString()) / f;
			return res * (neg ? -1 : 1);
		}

		public boolean ready() throws IOException {
			return br.ready();
		}

	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Test1 {
	static ArrayList<Integer>[] adjList;
	static boolean[] visited;
	static boolean yes;

	static void dfs(int a) {
		visited[a] = true;
		for (int x : adjList[a])
			if (!visited[x]) {
				dfs(x);
				yes = true;
			}
	}

	public static void main(String[] args) throws IOException {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		adjList = new ArrayList[100111];
		for (int i = 0; i < adjList.length; i++) {
			adjList[i] = new ArrayList<Integer>();
		}
		HashSet<Integer> hs = new HashSet<Integer>();
		boolean f = false;
		for (int i = 1; i <= n; i++) {
			int k = sc.nextInt();
			hs.add(i*1001);
			int[] tmp = new int[k];
			if(k>0)
				f = true;
			for (int j = 0; j < k; j++) {
				tmp[j] = sc.nextInt() ;
			}
			for (int j = 0; j < k; j++) {
				adjList[i * 1001].add(tmp[j]);
				adjList[tmp[j]].add(i * 1001);
			}
		}
		visited = new boolean[adjList.length];
		int c = 0;
		for (int i = 0; i <visited.length ; i++) {
			if (!visited[i] && hs.contains(i)) {
				dfs(i);
				c++;
			}
		}
		System.out.println(f?c - 1:c);
	}

	static class Scanner {
		StringTokenizer st;
		BufferedReader br;

		public Scanner(InputStream s) {
			br = new BufferedReader(new InputStreamReader(s));
		}

		public String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}

		public int nextInt() throws IOException {
			return Integer.parseInt(next());
		}

		public long nextLong() throws IOException {
			return Long.parseLong(next());
		}

		public String nextLine() throws IOException {
			return br.readLine();
		}

		public double nextDouble() throws IOException {
			String x = next();
			StringBuilder sb = new StringBuilder("0");
			double res = 0, f = 1;
			boolean dec = false, neg = false;
			int start = 0;
			if (x.charAt(0) == '-') {
				neg = true;
				start++;
			}
			for (int i = start; i < x.length(); i++)
				if (x.charAt(i) == '.') {
					res = Long.parseLong(sb.toString());
					sb = new StringBuilder("0");
					dec = true;
				} else {
					sb.append(x.charAt(i));
					if (dec)
						f *= 10;
				}
			res += Long.parseLong(sb.toString()) / f;
			return res * (neg ? -1 : 1);
		}

		public boolean ready() throws IOException {
			return br.ready();
		}

	}

}


import java.io.*;
import java.util.*;
public class test1 {
	static ArrayList<Integer>[] d;
	static int []M;
	static boolean[]vis;
	public static void main(String[] args) throws IOException, InterruptedException {
	Scanner sc=new Scanner (System.in);
	int n=sc.nextInt();
	int m=sc.nextInt();
	vis=new boolean[n];
	M=new int[n];
	d=new ArrayList[n];
	for(int i=0;i<n;i++)
		d[i]=new ArrayList();

	TreeSet <Integer>[]h=new TreeSet[n];
	for(int i=0;i<n;i++)
		h[i]=new TreeSet();
	for(int i=0;i<n;i++) {
		int y=sc.nextInt();
		for(int j=0;j<y;j++) {
			h[i].add(sc.nextInt());
		}
	}
	int v=0;
	for(int o=0;o<n;o++)
		if(h[o].size()==0)
			v++;
	for(int i=0;i<n-1;i++) {
		for(int w:h[i]) {
		for(int j=i+1;j<n;j++) {
			if(h[j].contains(w)) {
				d[i].add(j);
				d[j].add(i);

			}
			}}
	}
	int money=-1;
	for(int i=0;i<n;i++)
		if(!vis[i]) {
			dfs(i);
			money++;}
	if(v!=n)
		System.out.println(money);
	else
		System.out.println(n);
	}
   public static void dfs(int u) {
	   vis[u]=true;
	   for(int r:d[u]) {
		   if(!vis[r]) {
			   dfs(r);
			  }
	   }
   }
	static	class Scanner{
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream system) {br = new BufferedReader(new InputStreamReader(system));}
        public Scanner(String file) throws Exception {br = new BufferedReader(new FileReader(file));}
        public String next() throws IOException
        {
            while (st == null || !st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }
        public String nextLine()throws IOException{return br.readLine();}
        public int nextInt() throws IOException {return Integer.parseInt(next());}
        public double nextDouble() throws IOException {return Double.parseDouble(next());}
        public char nextChar()throws IOException{return next().charAt(0);}
        public Long nextLong()throws IOException{return Long.parseLong(next());}
        public boolean ready() throws IOException{return br.ready();}
        public void waitForInput() throws InterruptedException {Thread.sleep(3000);}
    }
}


import java.io.*;
import java.util.*;
public class test1 {
	static ArrayList<Integer>[] d;
	static int []M;
	static boolean[]vis;
	public static void main(String[] args) throws IOException, InterruptedException {
	Scanner sc=new Scanner (System.in);
	int n=sc.nextInt();
	int m=sc.nextInt();
	vis=new boolean[n];
	M=new int[n];
	d=new ArrayList[n];
	for(int i=0;i<n;i++)
		d[i]=new ArrayList();
	for(int i=0;i<m;i++)
	    M[i]=i+1;
	TreeSet <Integer>[]h=new TreeSet[n];
	for(int i=0;i<n;i++)
		h[i]=new TreeSet();
	for(int i=0;i<n;i++) {
		int y=sc.nextInt();
		for(int j=0;j<y;j++) {
			h[i].add(sc.nextInt());
		}
	}
	int v=0;
	for(int o=0;o<n;o++)
		if(h[o].size()==0)
			v++;
	for(int i=0;i<n-1;i++) {
		for(int w:h[i]) {
		for(int j=i+1;j<n;j++) {
			if(h[j].contains(w)) {
				d[i].add(j);
				d[j].add(i);

			}
			}}
	}
	int money=-1;
	for(int i=0;i<n;i++)
		if(!vis[i]) {
			dfs(i);
			money++;}
	if(v!=n)
		System.out.println(money);
	else
		System.out.println(n);
	}
   public static void dfs(int u) {
	   vis[u]=true;
	   for(int r:d[u]) {
		   if(!vis[r]) {
			   dfs(r);
			  }
	   }
   }
	static	class Scanner{
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream system) {br = new BufferedReader(new InputStreamReader(system));}
        public Scanner(String file) throws Exception {br = new BufferedReader(new FileReader(file));}
        public String next() throws IOException
        {
            while (st == null || !st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }
        public String nextLine()throws IOException{return br.readLine();}
        public int nextInt() throws IOException {return Integer.parseInt(next());}
        public double nextDouble() throws IOException {return Double.parseDouble(next());}
        public char nextChar()throws IOException{return next().charAt(0);}
        public Long nextLong()throws IOException{return Long.parseLong(next());}
        public boolean ready() throws IOException{return br.ready();}
        public void waitForInput() throws InterruptedException {Thread.sleep(3000);}
    }
}


import java.io.*;
import java.util.*;
public class test1 {
	static ArrayList<Integer>[] d;
	static int []M;
	static boolean[]vis;
	public static void main(String[] args) throws IOException, InterruptedException {
	Scanner sc=new Scanner (System.in);
	int n=sc.nextInt();
	int m=sc.nextInt();
	vis=new boolean[n];
	M=new int[n];
	d=new ArrayList[n];
	for(int i=0;i<n;i++)
		d[i]=new ArrayList();
	for(int i=0;i<m;i++)
	    M[i]=i+1;
	TreeSet <Integer>[]h=new TreeSet[n];
	for(int i=0;i<n;i++)
		h[i]=new TreeSet();
	for(int i=0;i<n;i++) {
		int y=sc.nextInt();
		for(int j=0;j<y;j++) {
			h[i].add(sc.nextInt());
		}
	}
	for(int i=0;i<n-1;i++) {
		for(int w:h[i]) {
		for(int j=i+1;j<n;j++) {
			if(h[j].contains(w)) {
				d[i].add(j);
				d[j].add(i);

			}
			}}
	}
	int money=-1;
	for(int i=0;i<n;i++)
		if(!vis[i]) {
			dfs(i);
			money++;}
		System.out.println(money);
	}
   public static void dfs(int u) {
	   vis[u]=true;
	   for(int r:d[u]) {
		   if(!vis[r]) {
			   dfs(r);
			  }
	   }
   }
	static	class Scanner{
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream system) {br = new BufferedReader(new InputStreamReader(system));}
        public Scanner(String file) throws Exception {br = new BufferedReader(new FileReader(file));}
        public String next() throws IOException
        {
            while (st == null || !st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }
        public String nextLine()throws IOException{return br.readLine();}
        public int nextInt() throws IOException {return Integer.parseInt(next());}
        public double nextDouble() throws IOException {return Double.parseDouble(next());}
        public char nextChar()throws IOException{return next().charAt(0);}
        public Long nextLong()throws IOException{return Long.parseLong(next());}
        public boolean ready() throws IOException{return br.ready();}
        public void waitForInput() throws InterruptedException {Thread.sleep(3000);}
    }
}


import java.util.*;
public final class Test1 {

    public static void dfs(int graph[][],int start,int n,boolean visited[]){

        visited[start]=true;
        for(int i=0;i<n;i++){
            if(graph[start][i]==1  && !visited[i]){
                dfs(graph,i,n,visited);
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
       ArrayList<Integer> arr[]=new ArrayList[n];

       int count=0;
       for(int i=0;i<n;i++){
            arr[i]=new ArrayList();
           int k=sc.nextInt();
           count+=k;
            for(int j=0;j<k;j++){
                arr[i].add(sc.nextInt());
            }
       }

       int graph[][]=new int[n][n];

       for(int i=0;i<n;i++){
           for(int j=0;j<n;j++){
               graph[i][j]=0;
           }
       }

       for(int i=0;i<n;i++){
           int size=arr[i].size();
           for(int j=0;j<size;j++){
               int x=arr[i].get(j);
               for(int k=0;k<n;k++){
                   if(i!=k && arr[k].contains(x)){
                       graph[i][k]=1;
                   }
               }
           }
       }

       boolean visited[]=new boolean[n];

       int connected=0;
       for(int i=0;i<n;i++){
           if(!visited[i]){
               connected++;

               dfs(graph,i,n,visited);

           }
       }

    /*  for(int i=0;i<n;i++){
          for(int j=0;j<n;j++){
              System.out.print(graph[i][j]+" ");
          }
          System.out.println();
      }*/
          if(count==0)
        System.out.println(connected);
        else
              System.out.println(connected-1);

    }
}


import java.util.*;
public final class Test1 {

    public static void dfs(int graph[][],int start,int n,boolean visited[]){

        visited[start]=true;
        for(int i=0;i<n;i++){
            if(graph[start][i]==1  && !visited[i]){
                dfs(graph,i,n,visited);
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
       ArrayList<Integer> arr[]=new ArrayList[n];

       for(int i=0;i<n;i++){
            arr[i]=new ArrayList();
           int k=sc.nextInt();
            for(int j=0;j<k;j++){
                arr[i].add(sc.nextInt());
            }
       }

       int graph[][]=new int[n][n];

       for(int i=0;i<n;i++){
           for(int j=0;j<n;j++){
               graph[i][j]=0;
           }
       }

       for(int i=0;i<n;i++){
           int size=arr[i].size();
           for(int j=0;j<size;j++){
               int x=arr[i].get(j);
               for(int k=0;k<n;k++){
                   if(i!=k && arr[k].contains(x)){
                       graph[i][k]=1;
                   }
               }
           }
       }

       boolean visited[]=new boolean[n];

       int count=0;
       for(int i=0;i<n;i++){
           if(!visited[i]){
               count++;

               dfs(graph,i,n,visited);

           }
       }

      for(int i=0;i<n;i++){
          for(int j=0;j<n;j++){
              System.out.print(graph[i][j]+" ");
          }
          System.out.println();
      }

        System.out.println(count-1);

    }
}


import java.util.*;
public final class Test1 {

    public static void dfs(int graph[][],int start,int n,boolean visited[]){

        visited[start]=true;
        for(int i=0;i<n;i++){
            if(graph[start][i]==1  && !visited[i]){
                dfs(graph,i,n,visited);
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
       ArrayList<Integer> arr[]=new ArrayList[n];

       for(int i=0;i<n;i++){
            arr[i]=new ArrayList();
           int k=sc.nextInt();
            for(int j=0;j<k;j++){
                arr[i].add(sc.nextInt());
            }
       }

       int graph[][]=new int[n][n];

       for(int i=0;i<n;i++){
           for(int j=0;j<n;j++){
               graph[i][j]=0;
           }
       }

       for(int i=0;i<n;i++){
           int size=arr[i].size();
           for(int j=0;j<size;j++){
               int x=arr[i].get(j);
               for(int k=0;k<n;k++){
                   if(i!=k && arr[k].contains(x)){
                       graph[i][k]=1;
                   }
               }
           }
       }

       boolean visited[]=new boolean[n];

       int count=0;
       for(int i=0;i<n;i++){
           if(!visited[i]){
               count++;

               dfs(graph,i,n,visited);

           }
       }

    /*  for(int i=0;i<n;i++){
          for(int j=0;j<n;j++){
              System.out.print(graph[i][j]+" ");
          }
          System.out.println();
      }*/

        System.out.println(count-1);

    }
}


import java.util.*;
public final class Test1 {

    public static void dfs(int graph[][],int start,int n,boolean visited[]){

        visited[start]=true;
        for(int i=0;i<n;i++){
            if(graph[start][i]==1  && !visited[i]){
                dfs(graph,i,n,visited);
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
       ArrayList<Integer> arr[]=new ArrayList[n];

       for(int i=0;i<n;i++){
            arr[i]=new ArrayList();
           int k=sc.nextInt();
            for(int j=0;j<k;j++){
                arr[i].add(sc.nextInt());
            }
       }

       int graph[][]=new int[n][n];

       for(int i=0;i<n;i++){
           for(int j=0;j<n;j++){
               graph[i][j]=0;
           }
       }

       for(int i=0;i<n;i++){
           int size=arr[i].size();
           for(int j=0;j<size;j++){
               int x=arr[i].get(j);
               for(int k=0;k<n;k++){
                   if(i!=k && arr[k].contains(x)){
                       graph[i][k]=1;
                   }
               }
           }
       }

       boolean visited[]=new boolean[n];

       int count=0;
       for(int i=0;i<n;i++){
           if(!visited[i]){
               count++;

               dfs(graph,i,n,visited);

           }
       }

    /*  for(int i=0;i<n;i++){
          for(int j=0;j<n;j++){
              System.out.print(graph[i][j]+" ");
          }
          System.out.println();
      }*/
          if(count!=n)
        System.out.println(count-1);
        else
              System.out.println(count);

    }
}


import java.util.*;
public final class Test1 {

    public static void dfs(int arr[],int n,int start,boolean visited[]){

        visited[start]=true;
        for(int i=0;i<n;i++){
            if(!visited[i]  &&  (arr[start]  & arr[i])>0  ){
                dfs(arr,n,i,visited);
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        int arr[]=new int[n];

        int count=0;

        for(int i=0;i<n;i++){
        arr[i]=0;
            int k=sc.nextInt();
            count+=k;

            for(int j=0;j<k;j++){
                int index=sc.nextInt()-1;
                arr[i]=1<<index|arr[i];
            }

        }
        //for(int i=0;i<n;i++)
        //System.out.println(arr[i]);

        boolean visited[]=new boolean[n];
        int connected=0;

        for(int i=0;i<n;i++){

            if(!visited[i])
            {
                connected++;
                dfs(arr,n,i,visited);

            }
        }

        if(count==0)
            System.out.println(connected);
        else
            System.out.println(connected-1);

    }
}


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author dipankar12
 */
import java.io.*;
import java.util.*;
public class r277a {
    public static void main(String args[])
    {
        fastio in=new fastio(System.in);
        PrintWriter pw=new PrintWriter(System.out);
        r277a1 ob=new r277a1();
        int n=in.nextInt();
        int m=in.nextInt();

        int count=0;
        for(int i=0;i<n;i++)
        {
            int num=in.nextInt();
            if(num==0)
                count++;
            else if(num==1)
                ob.makeset(in.nextInt());
            else
            {
                int lang=in.nextInt();
                ob.makeset(lang);
                for(int j=0;j<num-1;j++)
                {
                    int lang1=in.nextInt();
                    if(ob.findset(lang1)==-100)
                        ob.makeset(lang1);
                    ob.union(lang, lang1);
                    lang=lang1;
                }
                //System.out.println("Hello for "+(i+1));
            }
        }

        HashSet<Long> hs=new HashSet<Long>();
        for(int i=0;i<m;i++)
        {
            long x=ob.findset(i+1);
            if(x!=(i+1))
                hs.add(x);
        }
        //System.out.println("size is"+hs.size()+"count is "+count);
        int size=hs.size();
        if(size!=0)
            size--;

        pw.println(size+count);
        pw.close();
    }

    static class fastio {

            private final InputStream stream;
            private final byte[] buf = new byte[8192];
            private int cchar, snchar;
            private SpaceCharFilter filter;

            public fastio(InputStream stream) {
                this.stream = stream;
            }

            public int nxt() {
                if (snchar == -1)
                    throw new InputMismatchException();
                if (cchar >= snchar) {
                    cchar = 0;
                    try {
                        snchar = stream.read(buf);
                    } catch (IOException e) {
                        throw new InputMismatchException();
                    }
                    if (snchar <= 0)
                        return -1;
                }
                return buf[cchar++];
            }

            public int nextInt() {
                int c = nxt();
                while (isSpaceChar(c)) {
                    c = nxt();
                }
                int sgn = 1;
                if (c == '-') {
                    sgn = -1;
                    c = nxt();
                }
                int res = 0;
                do {
                    if (c < '0' || c > '9')
                        throw new InputMismatchException();
                    res *= 10;
                    res += c - '0';
                    c = nxt();
                } while (!isSpaceChar(c));
                return res * sgn;
            }

            public long nextLong() {
                int c = nxt();
                while (isSpaceChar(c)) {
                    c = nxt();
                }
                int sgn = 1;
                if (c == '-') {
                    sgn = -1;
                    c = nxt();
                }
                long res = 0;
                do {
                    if (c < '0' || c > '9')
                        throw new InputMismatchException();
                    res *= 10;
                    res += c - '0';
                    c = nxt();
                } while (!isSpaceChar(c));
                return res * sgn;
            }

            public int[] nextIntArray(int n) {
                int a[] = new int[n];
                for (int i = 0; i < n; i++) {
                    a[i] = nextInt();
                }
                return a;
            }

            public String readString() {
                int c = nxt();
                while (isSpaceChar(c)) {
                    c = nxt();
                }
                StringBuilder res = new StringBuilder();
                do {
                    res.appendCodePoint(c);
                    c = nxt();
                } while (!isSpaceChar(c));
                return res.toString();
            }

            public String nextLine() {
                int c = nxt();
                while (isSpaceChar(c))
                    c = nxt();
                StringBuilder res = new StringBuilder();
                do {
                    res.appendCodePoint(c);
                    c = nxt();
                } while (!isEndOfLine(c));
                return res.toString();
            }

            public boolean isSpaceChar(int c) {
                if (filter != null)
                    return filter.isSpaceChar(c);
                return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
            }

            private boolean isEndOfLine(int c) {
                return c == '\n' || c == '\r' || c == -1;
            }

            public interface SpaceCharFilter {
                public boolean isSpaceChar(int ch);
            }
        }

}
class r277a2
{
    long data;
    r277a2 parent;
    int rank;
}
class r277a1 {
    private HashMap<Long,r277a2> hm=new HashMap<Long,r277a2>();

    public void makeset(long data)
    {
        r277a2 ob=new r277a2();
        ob.data=data;
        ob.parent=ob;
        ob.rank=0;
        hm.put(data, ob);
    }

    boolean union(long data1,long data2)
    {
        r277a2 ob1=hm.get(data1);
        r277a2 ob2=hm.get(data2);

        r277a2 parent1=findset(ob1);
        r277a2 parent2=findset(ob2);

        if(parent1.data==parent2.data)
            return false;

        if(parent1.rank>=parent2.rank)
        {
            parent1.rank=(parent1.rank==parent2.rank)?parent1.rank+1:parent1.rank;
            parent2.parent=parent1;
            //System.out.println("Making union of"+parent2.data+" "+parent1.data);
        }
        else
            parent1.parent=parent2;
        return true;
    }

    public long findset(long data)
    {
        if(hm.get(data)==null)
            return -100;
        return findset(hm.get(data)).data;
    }

    r277a2 findset(r277a2 ob)
    {
        r277a2 parent=ob.parent;
        if(parent==ob)
            return parent;

        ob.parent=findset(ob.parent);
        return ob.parent;
    }

    void print()
    {
        System.out.println(hm);
    }
}