#include <bits/stdc++.h>
using namespace std;
const int N=1e5 +100;
const double eps=1e-7;
struct node{
    double v,id;
    node(double _v,double _q){v=_v;id=_q;}
    node(){}
    bool operator<(const node&b)const{
        return v<b.v;
    }
}a[N];
void Print(node x,node y)
{
    cout<<1<<" "<<x.id<<" "<<y.id<<endl;
}
void Delet(node x)
{
    cout<<2<<" "<<x.id<<endl;
}
vector<node>v0,v1;///0 - +
set<node>v2;
int main()
{
    ///ij 乘积放j 删了i
    ///随便删一个数 不超过一次
    ///奇数个负数 && 有 0 绝对值最小的负数跟0走 然后删0
    ///偶数个负数
    int n,pos,num;
    ///第一个大于等于0的位置 0的数量
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
            cin>>a[i].v,a[i].id=i+1;
        sort(a,a+n);
        for(int i=0;i<n;i++)
        {
            if(a[i].v>=0)
            {
                pos=i;
                break;
            }
        }
        for(int i=0;i<pos;i++)///-
        {
            v1.push_back(a[i]);
        }
        int sz=v1.size();
        num=0;
        int t=pos;
        while(abs(a[t].v)<eps&&t<n)///0
        {
            v0.push_back(a[t]);
            num++,t++;
        }
        for(int i=t;i<n;i++)///+
        {
            v2.insert(a[i]);
        }
        if(num)///如果有0 留一个0
        {
            while(num>1)
            {
                node x=v0.back();
                v0.pop_back();
                node y=v0.back();
                Print(x,y);
                num--;
            }
        }
        if(pos==1)///只有一个负数
        {
            Delet(v1.back());///删了 走
            v1.pop_back();
        }
        if(v2.empty())///没有正数
        {
            if(v1.empty())///没有负数 直接走
                continue;
        }
        ///v1 v2都是从小到大的
        if(int(v1.size())&1)///奇数个负数
        {
            if(num)///如果有0
            {
                node x=v1.back();
                node y=v0.back();
                Print(x,y);
                Delet(x);
                v1.pop_back();
                num--;
            }
            else///没0
            {
                node x=v1.back();
                Delet(x);
            }
        }
        if(num)
        {
            node x=v0.back();
            Delet(x);
        }
//         删除第二个元素 (vec[1])
//        vec.erase(vec.begin() + 1);
        ///一定剩下偶数个负数
        while(!v1.empty())
        {
            int sz=v1.size();
            node x=v1[0];
            node y=v1.back();
            Print(x,y);
            v2.insert(node(x.v*y.v,y.id));
//            v2.push_back(x.v*y.v);
            v1.erase(v1.begin());
//            v1.erase(--v1.end());
            v1.pop_back();
        }
        if(sz==0)continue;///其实就是只有0
        while(sz!=1)
        {
            node x=*v2.begin();
            node y=*(--v2.end());
            Print(x,y);
            v2.erase(v2.begin());
            v2.erase(--v2.end());
            v2.insert(node(x.v*y.v,y.id));
            sz=v2.size();
        }
    }
    return 0;
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
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) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskA solver = new TaskA();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskA {
        int[] parent;

        public void solve(int testNumber, InputReader in, PrintWriter out) {
            int n = in.nextInt();
            int m = in.nextInt();
            parent = new int[m + 1];
            int ans = 0;
            for (int i = 0; i < n; i++) {
                int k = in.nextInt();
                if (k == 0) ans++;
                int[] a = new int[k];
                for (int j = 0; j < k; j++) {
                    a[j] = in.nextInt();
                    if (parent[a[j]] == 0) parent[a[j]] = a[j];
                }
                for (int j = 1; j < k; j++) {
                    union_sets(a[0], a[j]);
                }
            }
            int leaders = 0;
            for (int i = 1; i <= m; i++) {
                if (parent[i] == i) leaders++;
            }
            if (leaders > 1) ans += leaders - 1;
            out.println(ans);
        }

        int find_set(int v) {
            if (v == parent[v])
                return v;
            return parent[v] = find_set(parent[v]);
        }

        void union_sets(int a, int b) {
            a = find_set(a);
            b = find_set(b);
            if (a != b)
                parent[b] = a;
        }

    }

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

        public InputReader(InputStream inputStream) {
            reader = new BufferedReader(new InputStreamReader(inputStream));
        }

        private void fillTokenizer() {
            if (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }

        public String next() {
            fillTokenizer();
            return tokenizer.nextToken();
        }

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

    }
}


import java.io.*;
import java.util.*;
public class MyClass {

    public static void main(String args[]) {
      FastReader sc = new FastReader();
      int n = sc.nextInt();
      int m = sc.nextInt();
      ArrayList<Integer> adj[] = new ArrayList[n+1];
      for (int i = 1; i <=n; i++) {
            adj[i] = new ArrayList<Integer>();
      }
      for(int i=1;i<=n;i++){
          int num = sc.nextInt();
          for(int j=0;j<num;j++){
              int l = sc.nextInt();
              adj[l].add(i);
          }
      }
      int arr[] = new int[n+1];
      int size[] = new int[n+1];
      Dsu d = new Dsu(n);
      for(int i=1;i<=n;i++){
          arr[i]=i;
          size[i]=1;
      }
      for(int i=1;i<=m;i++){
          if(adj[i].size()<=1){continue;}
           for(int j=1;j<adj[i].size();j++){
               int x = adj[i].get(j-1);
               int y = adj[i].get(j);
               d.union(x,y,size,arr);
           }
      }
      int ans = d.components()-1;
      System.out.println(ans);

    }
}
class Dsu{
	private int numcomponents;
	Dsu(int num){
		numcomponents = num;
	}
	public int components(){
	    return numcomponents;
	}
    public int root(int arr[],int num){
        int i = num;
        while(i != arr[i]){
            arr[i] = arr[arr[i]];
            i = arr[i];
        }
        return i;
    }
    public void union(int x, int y, int size[],int arr[]){
        int roota = root(arr,x);
        int rootb = root(arr,y);
        if(roota==rootb){return;}
        if(size[roota]<size[rootb]){
            arr[roota] = rootb;
            size[rootb] += size[roota];
        }
        else{
            arr[rootb] = roota;
            size[roota] += size[rootb];
        }
		numcomponents--;
    }

}

class FastReader
	{
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			br = new BufferedReader(new
					InputStreamReader(System.in));
		}

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

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

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

		double nextDouble()
		{
			return Double.parseDouble(next());
		}

		String nextLine()
		{
			String str = "";
			try
			{
				str = br.readLine();
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			return str;
		}
	}


import java.io.*;
import java.util.*;
public class MyClass {

    public static void main(String args[]) {
      FastReader sc = new FastReader();
      int n = sc.nextInt();
      int m = sc.nextInt();
      ArrayList<Integer> adj[] = new ArrayList[n+1];
      for (int i = 1; i <=n; i++) {
            adj[i] = new ArrayList<Integer>();
      }
      for(int i=1;i<=n;i++){
          int num = sc.nextInt();
          for(int j=0;j<num;j++){
              int l = sc.nextInt();
              adj[l].add(i);
          }
      }
      int arr[] = new int[n+1];
      int size[] = new int[n+1];
      Dsu d = new Dsu(n);
      for(int i=1;i<=n;i++){
          arr[i]=i;
          size[i]=1;
      }
      int f=0;
      for(int i=1;i<=m;i++){
          if(adj[i].size()==0){
              f++;
          }
          if(adj[i].size()<=1){continue;}
           for(int j=1;j<adj[i].size();j++){
               int x = adj[i].get(j-1);
               int y = adj[i].get(j);
               d.union(x,y,size,arr);
           }
      }
      if(f==n){
          System.out.println(n);
          return;
      }
      int ans = d.components()-1;
      System.out.println(ans);

    }
}
class Dsu{
	private int numcomponents;
	Dsu(int num){
		numcomponents = num;
	}
	public int components(){
	    return numcomponents;
	}
    public int root(int arr[],int num){
        int i = num;
        while(i != arr[i]){
            arr[i] = arr[arr[i]];
            i = arr[i];
        }
        return i;
    }
    public void union(int x, int y, int size[],int arr[]){
        int roota = root(arr,x);
        int rootb = root(arr,y);
        if(roota==rootb){return;}
        if(size[roota]<size[rootb]){
            arr[roota] = rootb;
            size[rootb] += size[roota];
        }
        else{
            arr[rootb] = roota;
            size[roota] += size[rootb];
        }
		numcomponents--;
    }

}

class FastReader
	{
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			br = new BufferedReader(new
					InputStreamReader(System.in));
		}

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

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

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

		double nextDouble()
		{
			return Double.parseDouble(next());
		}

		String nextLine()
		{
			String str = "";
			try
			{
				str = br.readLine();
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			return str;
		}
	}


import java.io.*;
import java.util.*;
public class MyClass {

    public static void main(String args[]) {
      FastReader sc = new FastReader();
      int n = sc.nextInt();
      int m = sc.nextInt();
      ArrayList<Integer> adj[] = new ArrayList[m+1];
      for (int i = 1; i <=m; i++) {
            adj[i] = new ArrayList<Integer>();
      }
      for(int i=1;i<=n;i++){
          int num = sc.nextInt();
          for(int j=0;j<num;j++){
              int l = sc.nextInt();
              adj[l].add(i);
          }
      }
      int arr[] = new int[n+1];
      int size[] = new int[n+1];
      Dsu d = new Dsu(n);
      for(int i=1;i<=n;i++){
          arr[i]=i;
          size[i]=1;
      }
      int f=0;
      for(int i=1;i<=m;i++){
          if(adj[i].size()==0){
              f++;
          }
          if(adj[i].size()<=1){continue;}
           for(int j=1;j<adj[i].size();j++){
               int x = adj[i].get(j-1);
               int y = adj[i].get(j);
               d.union(x,y,size,arr);
           }
      }
      if(f==n){
          System.out.println(n);
          return;
      }
      int ans = d.components()-1;
      System.out.println(ans);

    }
}
class Dsu{
	private int numcomponents;
	Dsu(int num){
		numcomponents = num;
	}
	public int components(){
	    return numcomponents;
	}
    public int root(int arr[],int num){
        int i = num;
        while(i != arr[i]){
            arr[i] = arr[arr[i]];
            i = arr[i];
        }
        return i;
    }
    public void union(int x, int y, int size[],int arr[]){
        int roota = root(arr,x);
        int rootb = root(arr,y);
        if(roota==rootb){return;}
        if(size[roota]<size[rootb]){
            arr[roota] = rootb;
            size[rootb] += size[roota];
        }
        else{
            arr[rootb] = roota;
            size[roota] += size[rootb];
        }
		numcomponents--;
    }

}

class FastReader
	{
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			br = new BufferedReader(new
					InputStreamReader(System.in));
		}

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

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

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

		double nextDouble()
		{
			return Double.parseDouble(next());
		}

		String nextLine()
		{
			String str = "";
			try
			{
				str = br.readLine();
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			return str;
		}
	}


import java.io.*;
import java.util.*;
public class MyClass {

    public static void main(String args[]) {
      FastReader sc = new FastReader();
      int n = sc.nextInt();
      int m = sc.nextInt();
      ArrayList<Integer> adj[] = new ArrayList[m+1];
      for (int i = 1; i <=m; i++) {
            adj[i] = new ArrayList<Integer>();
      }
      for(int i=1;i<=n;i++){
          int num = sc.nextInt();
          for(int j=0;j<num;j++){
              int l = sc.nextInt();
              adj[l].add(i);
          }
      }
      int arr[] = new int[n+1];
      int size[] = new int[n+1];
      Dsu d = new Dsu(n);
      for(int i=1;i<=n;i++){
          arr[i]=i;
          size[i]=1;
      }
      int f=0;
      for(int i=1;i<=m;i++){
          if(adj[i].size()==0){
              f++;
          }
          if(adj[i].size()<=1){continue;}
           for(int j=1;j<adj[i].size();j++){
               int x = adj[i].get(j-1);
               int y = adj[i].get(j);
               d.union(x,y,size,arr);
           }
      }
      if(f==m){
          System.out.println(n);
          return;
      }
      int ans = d.components()-1;
      System.out.println(ans);

    }
}
class Dsu{
	private int numcomponents;
	Dsu(int num){
		numcomponents = num;
	}
	public int components(){
	    return numcomponents;
	}
    public int root(int arr[],int num){
        int i = num;
        while(i != arr[i]){
            arr[i] = arr[arr[i]];
            i = arr[i];
        }
        return i;
    }
    public void union(int x, int y, int size[],int arr[]){
        int roota = root(arr,x);
        int rootb = root(arr,y);
        if(roota==rootb){return;}
        if(size[roota]<size[rootb]){
            arr[roota] = rootb;
            size[rootb] += size[roota];
        }
        else{
            arr[rootb] = roota;
            size[roota] += size[rootb];
        }
		numcomponents--;
    }

}

class FastReader
	{
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			br = new BufferedReader(new
					InputStreamReader(System.in));
		}

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

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

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

		double nextDouble()
		{
			return Double.parseDouble(next());
		}

		String nextLine()
		{
			String str = "";
			try
			{
				str = br.readLine();
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			return str;
		}
	}


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

/**
 * Created by akshaysharma on 28/11/16.
 */
public class Problem277A {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int M = sc.nextInt();
        boolean is_z = true;
        int[][] graph = new int[N][N];
        ArrayList<ArrayList<Integer>> arr = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            arr.add(i, new ArrayList<Integer>());
        }
        for (int i = 0; i < N; i++) {
            int m = sc.nextInt();
            if(m!=0){
                is_z = false;
            }
            for (int j = 0; j < m; j++) {
                int index = sc.nextInt() - 1;
                ArrayList<Integer> arr_2 = arr.get(index);
                for (int item : arr_2) {
                    if (item != i) {
                        graph[item][i] = 1;
                        graph[i][item] = 1;
                    }
                }
                arr_2.add(i);
            }
        }
        int count = 0;
        boolean[] visited = new boolean[N];
        for (int i = 0; i < N; i++) {
            if (!visited[i]) {
                visited[i] = !visited[i];
                count++;
                bfs(i, graph, visited, N);
            }
        }
        if(is_z){
            System.out.print(count);
        }
        else{
            System.out.print(count-1);
        }

    }

    public static void bfs(int i, int[][] graph, boolean[] visited, int N) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(i);
        while (!queue.isEmpty()) {
            int u = queue.remove();
            for (int v = 0; v < N; v++) {
                if (graph[u][v] != 0 && !visited[v]) {
                    visited[v] = true;
                    queue.add(v);
                }
            }
        }
    }

}


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

/**
 * Created by akshaysharma on 28/11/16.
 */
public class Problem277A {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int M = sc.nextInt();
        boolean is_z = true;
        int[][] graph = new int[N][N];
        ArrayList<ArrayList<Integer>> arr = new ArrayList<>();
        for (int i = 0; i < M; i++) {
            arr.add(i, new ArrayList<>(M));
        }
        for (int i = 0; i < N; i++) {
            int m = sc.nextInt();
            if(m!=0){
                is_z = false;
            }
            for (int j = 0; j < m; j++) {
                int index = sc.nextInt() - 1;
                ArrayList<Integer> arr_2 = arr.get(index);
                for (int item : arr_2) {
                    if (item != i) {
                        graph[item][i] = 1;
                        graph[i][item] = 1;
                    }
                }
                arr_2.add(i);
            }
        }
        int count = 0;
        boolean[] visited = new boolean[N];
        for (int i = 0; i < N; i++) {
            if (!visited[i]) {
                visited[i] = !visited[i];
                count++;
                bfs(i, graph, visited, N);
            }
        }
        if(is_z){
            System.out.print(count);
        }
        else{
            System.out.print(count-1);
        }

    }

    public static void bfs(int i, int[][] graph, boolean[] visited, int N) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(i);
        while (!queue.isEmpty()) {
            int u = queue.remove();
            for (int v = 0; v < N; v++) {
                if (graph[u][v] != 0 && !visited[v]) {
                    visited[v] = true;
                    queue.add(v);
                }
            }
        }
    }

}


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

/**
 * Created by akshaysharma on 28/11/16.
 */
public class Problem277A {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int M = sc.nextInt();
        int[][] graph = new int[N][N];
        ArrayList<ArrayList<Integer>> arr = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            arr.add(i, new ArrayList<Integer>());
        }
        for (int i = 0; i < N; i++) {
            int m = sc.nextInt();
            for (int j = 0; j < m; j++) {
                int index = sc.nextInt() - 1;
                ArrayList<Integer> arr_2 = arr.get(index);
                for (int item : arr_2) {
                    if (item != i) {
                        graph[item][i] = 1;
                        graph[i][item] = 1;
                    }
                }
                arr_2.add(i);
            }
        }
        int count = 0;
        boolean[] visited = new boolean[N];
        for (int i = 0; i < N; i++) {
            if (!visited[i]) {
                visited[i] = !visited[i];
                count++;
                bfs(i, graph, visited, N);
            }
        }
        System.out.print(count-1);

    }

    public static void bfs(int i, int[][] graph, boolean[] visited, int N) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(i);
        while (!queue.isEmpty()) {
            int u = queue.remove();
            for (int v = 0; v < N; v++) {
                if (graph[u][v] != 0 && !visited[v]) {
                    visited[v] = true;
                    queue.add(v);
                }
            }
        }
    }

}


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

/**
 * Created by akshaysharma on 28/11/16.
 */
public class Problem277A {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int M = sc.nextInt();
        int[][] graph = new int[N][N];
        ArrayList<ArrayList<Integer>> arr = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            arr.add(i, new ArrayList<Integer>());
        }
        for (int i = 0; i < N; i++) {
            int m = sc.nextInt();
            for (int j = 0; j < m; j++) {
                int index = sc.nextInt() - 1;
                ArrayList<Integer> arr_2 = arr.get(index);
                for (int item : arr_2) {
                    if (item != i) {
                        graph[item][i] = 1;
                        graph[i][item] = 1;
                    }
                }
                arr_2.add(i);
            }
        }
        int count = 0;
        boolean[] visited = new boolean[N];
        for (int i = 0; i < N; i++) {
            if (!visited[i]) {
                visited[i] = !visited[i];
                count++;
                bfs(i, graph, visited, N);
            }
        }
        if(count == N){
            System.out.print(count);
        }
        else{
            System.out.print(count-1);
        }

    }

    public static void bfs(int i, int[][] graph, boolean[] visited, int N) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(i);
        while (!queue.isEmpty()) {
            int u = queue.remove();
            for (int v = 0; v < N; v++) {
                if (graph[u][v] != 0 && !visited[v]) {
                    visited[v] = true;
                    queue.add(v);
                }
            }
        }
    }

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {
        //1: Declare/Define variables
        InputStreamReader reader = new InputStreamReader(System.in);
        BufferedReader in = new BufferedReader(reader);
        StringTokenizer st = new StringTokenizer(in.readLine());
        QuickUnion quick;
        HashSet<Integer> parents = new HashSet();
        int numOfEmployees = Integer.parseInt(st.nextToken()),
                numKnownLanguages,
                minCost = 0,
                mainLanguage,
                language;

        //2: Create a quickunion
        quick = new QuickUnion();

        //3: Unite relevant languages
        for (int i = 0; i < numOfEmployees; i++) {
            //A: Get the number of languages that employee i knows
            st = new StringTokenizer(in.readLine());
            numKnownLanguages = Integer.parseInt(st.nextToken());

            //B: Process the languages
            if (numKnownLanguages > 1) {
                //a: Get employee i's main language
                mainLanguage = Integer.parseInt(st.nextToken());

                //b: Check if employee i's main language exists
                if (!quick.existsInSet(mainLanguage)) {
                    quick.getParents().put(mainLanguage, mainLanguage);
                }

                //c: Unite or set the parent of each language
                for (int j = 1; j < numKnownLanguages; j++) {
                    language = Integer.parseInt(st.nextToken());

                    if (!quick.existsInSet(language)) {
                        quick.getParents().put(language, mainLanguage);
                    } else {
                        quick.unite(mainLanguage, language);
                    }

                }

            } else if (numKnownLanguages == 0) {
                //Increase the minimum cost if an employee doesn't know any languages
                minCost++;
            }

        }

        //4: Find the number of forests
        for (Map.Entry<Integer, Integer> entry : quick.getParents().entrySet()) {
            parents.add(quick.root(entry.getKey()));

        }

        //5: Print the result
        minCost += (parents.size() > 0) ? parents.size() - 1 : 0;
        System.out.println(minCost);

    }

}

class QuickUnion {

    //Attribute(s)
    private HashMap<Integer, Integer> parents;

    //Constructor
    public QuickUnion() {
        parents = new HashMap();
    }

    //Behaviors
    public int root(int node) {

        while (node != parents.get(node)) {
            node = parents.get(node);
        }

        return node;
    }

    public boolean find(int nodeA, int nodeB) {
        return root(nodeA) == root(nodeB);
    }

    public void unite(int nodeA, int nodeB) {
        //1: Find the parents of nodeA and nodeB
        int parentA = root(nodeA), parentB = root(nodeB);

        //2: Assign nodeB's parent as nodeA's parent
        parents.put(parentA, parentB);
    }

    public HashMap<Integer, Integer> getParents() {
        return parents;
    }

    public boolean existsInSet(int language) {
        return parents.containsKey(language);

    }

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {

    static InputStreamReader reader = new InputStreamReader(System.in);
    static BufferedReader in = new BufferedReader(reader);
    static StringTokenizer st;
    static undirectedGraph_ListBased languageGraph = new undirectedGraph_ListBased();

    public static int getMinCost(HashMap<Integer, Node> nodes) {
        //1: Declare/Define variables
        int minCost = 0;

        //2:Visit each node
        for (Map.Entry<Integer, Node> node : nodes.entrySet()) {

            if (node.getValue().status.equals("Unvisited")) {
                minCost++;
                depthFirstSearch_Visit(nodes, node.getValue());
            }
        }

        //3: Return the result
        return minCost - 1;

    }

    public static void depthFirstSearch_Visit(HashMap<Integer, Node> nodes, Node chosenNode) {
        //1: Change the status of the chosenNode to "visited"
        chosenNode.status = "Visited";

        //2: Explore the nodes connected to the chosenNode
        for (Map.Entry<Integer, Node> node : chosenNode.connectionList.entrySet()) {

            if (node.getValue().status.equals("Unvisited")) {
                depthFirstSearch_Visit(nodes, node.getValue());
            }
        }

        //3: Change the status of the chosenNode to "finished"
        chosenNode.status = "Finished";
    }

    public static int insertIntoGraph(int numOfEmployees, int numOfLanguages, int minCost) throws IOException {
        //1: Declare/Define variables
        int language, otherLanguages;

        //2: Insert languages into the graph
        for (int i = 0; i < numOfEmployees; i++) {
            //A: Get the number of languages that x employee knows
            st = new StringTokenizer(in.readLine());
            numOfLanguages = Integer.parseInt(st.nextToken());

            if (numOfLanguages == 0) {
                minCost++;
            } else {

                language = Integer.parseInt(st.nextToken());

                if (!languageGraph.getNodes().containsKey(language)) {
                    languageGraph.getNodes().put(language, new Node(language));
                }

                for (int j = 1; j < numOfLanguages; j++) {
                    //a: Get the other languages that the employee knows
                    otherLanguages = Integer.parseInt(st.nextToken());

                    //b: Check if the language exists in the language graph
                    if (!languageGraph.getNodes().containsKey(otherLanguages)) {
                        languageGraph.getNodes().put(otherLanguages, new Node(otherLanguages));
                    }
                    //c: Connect the language to the language added first on this line(and vice versa)
                    if (!languageGraph.getNodes().get(language).connectionList.containsKey(otherLanguages)) {
                        languageGraph.getNodes().get(language).connectionList.put(otherLanguages, languageGraph.getNodes().get(otherLanguages));
                        languageGraph.getNodes().get(otherLanguages).connectionList.put(language, languageGraph.getNodes().get(language));
                    }

                }
            }
        }

        //3: Return the initial minimum cost
        return minCost;
    }

    public static void main(String[] args) throws IOException {
        //1: Declare/Define variables
        st = new StringTokenizer(in.readLine());

        int numOfEmployees = Integer.parseInt(st.nextToken()), minCost = 0;

        //2: Insert the languages into the graph
        minCost = insertIntoGraph(numOfEmployees, 0, minCost) + getMinCost(languageGraph.nodes);

        //3: Print the result
        System.out.println(minCost);

    }
}

class Node {

    //Attributes
    //Can be replaced by any type of value
    int value;
    //Status indicates 3 states={Unvisited, Visited, Finished}
    String status = "Unvisited";
    Node predecessor = null;

    //A list of nodes connected to this node
    HashMap<Integer, Node> connectionList = new HashMap();

    //Constructor(s)
    Node(int value) {
        this.value = value;
    }

    //Methods
    //Getters
    public int getValue() {
        return value;
    }

    public HashMap<Integer, Node> getConnectionList() {
        return connectionList;
    }

    //Setters
    public void setValue(int value) {
        this.value = value;
    }

}

class undirectedGraph_ListBased {

    //Attributes
    HashMap<Integer, Node> nodes = new HashMap();

    //Behaviors
    public HashMap<Integer, Node> getNodes() {
        return nodes;
    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {
        //1: Declare/Define variables
        InputStreamReader reader = new InputStreamReader(System.in);
        BufferedReader in = new BufferedReader(reader);
        StringTokenizer st = new StringTokenizer(in.readLine());
        QuickUnion quick;
        HashSet<Integer> parents = new HashSet();
        int numOfEmployees = Integer.parseInt(st.nextToken()),
                numKnownLanguages,
                minCost = 0,
                mainLanguage,
                language;

        //2: Create a quickunion
        quick = new QuickUnion();

        //3: Unite relevant languages
        for (int i = 0; i < numOfEmployees; i++) {
            //A: Get the number of languages that employee i knows
            st = new StringTokenizer(in.readLine());
            numKnownLanguages = Integer.parseInt(st.nextToken());

            //B: Process the languages
            if (numKnownLanguages >=1) {
                //a: Get employee i's main language
                mainLanguage = Integer.parseInt(st.nextToken());

                //b: Check if employee i's main language exists
                if (!quick.existsInSet(mainLanguage)) {
                    quick.getParents().put(mainLanguage, mainLanguage);
                }

                //c: Unite or set the parent of each language
                for (int j = 1; j < numKnownLanguages; j++) {
                    language = Integer.parseInt(st.nextToken());

                    if (!quick.existsInSet(language)) {
                        quick.getParents().put(language, mainLanguage);
                    } else {
                        quick.unite(mainLanguage, language);
                    }

                }

            } else if (numKnownLanguages == 0) {
                //Increase the minimum cost if an employee doesn't know any languages
                minCost++;
            }

        }

        //4: Find the number of forests
        for (Map.Entry<Integer, Integer> entry : quick.getParents().entrySet()) {
            parents.add(quick.root(entry.getKey()));

        }

        //5: Print the result
        minCost += (parents.size() > 0) ? parents.size() - 1 : 0;
        System.out.println(minCost);

    }

}

class QuickUnion {

    //Attribute(s)
    private HashMap<Integer, Integer> parents;

    //Constructor
    public QuickUnion() {
        parents = new HashMap();
    }

    //Behaviors
    public int root(int node) {

        while (node != parents.get(node)) {
            node = parents.get(node);
        }

        return node;
    }

    public boolean find(int nodeA, int nodeB) {
        return root(nodeA) == root(nodeB);
    }

    public void unite(int nodeA, int nodeB) {
        //1: Find the parents of nodeA and nodeB
        int parentA = root(nodeA), parentB = root(nodeB);

        //2: Assign nodeB's parent as nodeA's parent
        parents.put(parentA, parentB);
    }

    public HashMap<Integer, Integer> getParents() {
        return parents;
    }

    public boolean existsInSet(int language) {
        return parents.containsKey(language);

    }

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {

    static InputStreamReader reader = new InputStreamReader(System.in);
    static BufferedReader in = new BufferedReader(reader);
    static StringTokenizer st;
    static undirectedGraph_ListBased languageGraph = new undirectedGraph_ListBased();

    public static int getMinCost(HashMap<Integer, Node> nodes) {
        //1: Declare/Define variables
        int minCost = 0;

        //2:Visit each node
        for (Map.Entry<Integer, Node> node : nodes.entrySet()) {

            if (node.getValue().status.equals("Unvisited")) {
                minCost++;
                depthFirstSearch_Visit(nodes, node.getValue());
            }
        }

        //3: Return the result
        return (minCost >= 1) ? minCost - 1 : 0;

    }

    public static void depthFirstSearch_Visit(HashMap<Integer, Node> nodes, Node chosenNode) {
        //1: Change the status of the chosenNode to "visited"
        chosenNode.status = "Visited";

        //2: Explore the nodes connected to the chosenNode
        for (Map.Entry<Integer, Node> node : chosenNode.connectionList.entrySet()) {

            if (node.getValue().status.equals("Unvisited")) {
                depthFirstSearch_Visit(nodes, node.getValue());
            }
        }

        //3: Change the status of the chosenNode to "finished"
        chosenNode.status = "Finished";
    }

    public static int insertIntoGraph(int numOfEmployees, int numOfLanguages, int minCost) throws IOException {
        //1: Declare/Define variables
        int language, otherLanguages;

        //2: Insert languages into the graph
        for (int i = 0; i < numOfEmployees; i++) {
            //A: Get the number of languages that x employee knows
            st = new StringTokenizer(in.readLine());
            numOfLanguages = Integer.parseInt(st.nextToken());

            if (numOfLanguages == 0) {
                minCost++;
            } else {

                language = Integer.parseInt(st.nextToken());

                if (!languageGraph.getNodes().containsKey(language)) {
                    languageGraph.getNodes().put(language, new Node(language));
                }

                for (int j = 1; j < numOfLanguages; j++) {
                    //a: Get the other languages that the employee knows
                    otherLanguages = Integer.parseInt(st.nextToken());

                    //b: Check if the language exists in the language graph
                    if (!languageGraph.getNodes().containsKey(otherLanguages)) {
                        languageGraph.getNodes().put(otherLanguages, new Node(otherLanguages));
                    }
                    //c: Connect the language to the language added first on this line(and vice versa)
                    if (!languageGraph.getNodes().get(language).connectionList.containsKey(otherLanguages)) {
                        languageGraph.getNodes().get(language).connectionList.put(otherLanguages, languageGraph.getNodes().get(otherLanguages));
                        languageGraph.getNodes().get(otherLanguages).connectionList.put(language, languageGraph.getNodes().get(language));
                    }

                }
            }
        }

        //3: Return the initial minimum cost
        return minCost;
    }

    public static void main(String[] args) throws IOException {
        //1: Declare/Define variables
        st = new StringTokenizer(in.readLine());

        int numOfEmployees = Integer.parseInt(st.nextToken()), minCost = 0;

        //2: Insert the languages into the graph
        minCost = insertIntoGraph(numOfEmployees, 0, minCost) + getMinCost(languageGraph.nodes);

        //3: Print the result
        System.out.println(minCost);

    }
}

class Node {

    //Attributes
    //Can be replaced by any type of value
    int value;
    //Status indicates 3 states={Unvisited, Visited, Finished}
    String status = "Unvisited";
    Node predecessor = null;

    //A list of nodes connected to this node
    HashMap<Integer, Node> connectionList = new HashMap();

    //Constructor(s)
    Node(int value) {
        this.value = value;
    }

    //Methods
    //Getters
    public int getValue() {
        return value;
    }

    public HashMap<Integer, Node> getConnectionList() {
        return connectionList;
    }

    //Setters
    public void setValue(int value) {
        this.value = value;
    }

}

class undirectedGraph_ListBased {

    //Attributes
    HashMap<Integer, Node> nodes = new HashMap();

    //Behaviors
    public HashMap<Integer, Node> getNodes() {
        return nodes;
    }
}


import java.util.*;

public class Test{

    static int graph[][];
    static boolean visited [];
    static int n,m;

    public static void dfs(int i){
        visited[i] = true;

            for(int j=0;j<m;j++){
                if(graph[i][j] == 1)
                    for(int k=0;k<n;k++){
                        if(graph[k][j] == 1){
                            if(!visited[k])
                                dfs(k);
                        }
                    }
            }
        }

    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        graph = new int[n][m];
        visited = new boolean[n];
        for(int i=0;i<n;i++){
            int nl = sc.nextInt();
            for(int j=0;j<nl;j++) graph[i][sc.nextInt()-1] = 1;
        }
        int ans = -1;
        boolean f = false;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                if(graph[i][j] == 1){
                    f = true;
                    break;
                }
        if(!f) ans++;
        for(int i=0;i<n;i++){
            if(!visited[i]){
                dfs(i);
                ans++;
            }
        }
        System.out.println(ans);
    }
}


import java.util.*;

public class Test{

    static int graph[][];
    static boolean visited [];
    static int n,m;

    public static void dfs(int i){
        visited[i] = true;

            for(int j=0;j<m;j++){
                if(graph[i][j] == 1)
                    for(int k=0;k<n;k++){
                        if(graph[k][j] == 1){
                            if(!visited[k])
                                dfs(k);
                        }
                    }
            }
        }

    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        graph = new int[n][m];
        visited = new boolean[n];
        for(int i=0;i<n;i++){
            int nl = sc.nextInt();
            for(int j=0;j<nl;j++) graph[i][sc.nextInt()-1] = 1;
        }
        int ans = -1;
        boolean f = false;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(graph[i][j] == 1){
                    f = true;
                    break;
                }
        if(!f) ans++;
        for(int i=0;i<n;i++){
            if(!visited[i]){
                dfs(i);
                ans++;
            }
        }
        System.out.println(ans);
    }
}


import java.util.*;

public class Test{

    static int graph[][];
    static boolean visited [];
    static int n,m;

    public static void dfs(int i){
        visited[i] = true;

            for(int j=0;j<m;j++){
                if(graph[i][j] == 1)
                    for(int k=0;k<n;k++){
                        if(graph[k][j] == 1){
                            if(!visited[k])
                                dfs(k);
                        }
                    }
            }
        }

    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        graph = new int[n][m];
        visited = new boolean[n];
        for(int i=0;i<n;i++){
            int nl = sc.nextInt();
            for(int j=0;j<nl;j++) graph[i][sc.nextInt()-1] = 1;
        }
        int ans = -1;
        for(int i=0;i<n;i++){
            if(!visited[i]){
                dfs(i);
                ans++;
            }
        }
        System.out.println(ans);
    }
}


import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.Set;
import java.util.Stack;

public class Solution {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		InputReader s = new InputReader(System.in);
		PrintWriter p = new PrintWriter(System.out);
		int ans = 0;
		int n = s.nextInt();
		int l = s.nextInt();
		HashMap<Integer, Integer>[] lang = new HashMap[n];
		for (int i = 0; i < n; i++) {
			lang[i] = new HashMap<>();
			int m = s.nextInt();
			for (int j = 0; j < m; j++) {
				lang[i].put(s.nextInt(), 1);
			}
		}
		ArrayList<pair> edges = new ArrayList<>();
		for (int i = 0; i < n; i++) {
			HashMap<Integer, Integer> temp = lang[i];
			Set<Integer> tempset = temp.keySet();
			for (int k : tempset) {
				for (int j = i + 1; j < n; j++) {
					if (lang[j].containsKey(k)) {
						edges.add(new pair(i, j));
						edges.add(new pair(j, i));
					}
				}
			}
		}
		int[] arr = new int[n];
		int[] size = new int[n];
		for (int i = 0; i < n; i++) {
			arr[i] = i;
			size[i] = 1;
		}
		for (int i = 0; i < edges.size(); i++) {
			union(arr, size, edges.get(i).x, edges.get(i).y);
		}
		for(int i = 0; i < arr.length; i++) {
			if(arr[i]==i) {
				ans++;
			}
		}
		System.out.println(ans-1);
		p.flush();
		p.close();
	}

	public static void union(int[] arr, int[] size, int a, int b) {
		int root_a = root(arr, a);
		int root_b = root(arr, b);
		if (size[root_a] >= size[root_b]) {
			arr[root_b] = root_a;
			size[root_a] += size[root_b];
		} else {
			arr[root_a] = root_b;
			size[root_b] += size[root_a];
		}
	}

	public static int root(int[] arr, int i) {
		while (arr[i] != i) {
			i = arr[i];
		}
		return i;
	}

	public static long power(long n, long x) {
		if (x == 0) {
			return 1;
		}
		long half = power(n, x / 2);
		if (x % 2 == 1) {
			return half * half * n;
		} else {
			return half * half;
		}
	}

	public static boolean check(ArrayList[] graph, int curr, int[] vis) {
		Stack<Integer> st = new Stack<>();
		long vert = 0;
		long edge = 0;
		vert = 1;
		st.push(curr);
		while (!st.isEmpty()) {
			int temp = st.pop();
			vis[temp] = 1;
			ArrayList<Integer> nbrs = graph[temp];
			edge += nbrs.size();
			for (int i = 0; i < nbrs.size(); i++) {
				int nbr = nbrs.get(i);
				if (vis[nbr] == 0) {
					st.push(nbr);
					vis[nbr] = 1;
					vert++;
				}
			}
		}
		if (edge == (vert * (vert - 1))) {
			return true;
		} else {
			return false;
		}
	}

	public static class node {
		int index;
		HashMap<Integer, Integer> nbrs;

		node(int i) {
			this.index = i;
			this.nbrs = new HashMap<>();
		}
	}

	static class pair implements Comparable<pair> {
		Integer x, y;

		pair(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public int compareTo(pair o) {
			int result = x.compareTo(o.x);
			if (result == 0)
				result = y.compareTo(o.y);

			return result;
		}

		public String toString() {
			return x + " " + y;
		}

		public boolean equals(Object o) {
			if (o instanceof pair) {
				pair p = (pair) o;
				return p.x - x == 0 && p.y - y == 0;
			}
			return false;
		}

		public int hashCode() {
			return new Long(x).hashCode() * 31 + new Long(y).hashCode();
		}
	}

	public static ArrayList Divisors(long n) {
		ArrayList<Long> div = new ArrayList<>();

		for (long i = 1; i <= Math.sqrt(n); i++) {
			if (n % i == 0) {
				div.add(i);

				if (n / i != i)
					div.add(n / i);
			}
		}
		return div;
	}

	public static int BinarySearch(long[] a, long k) {
		int n = a.length;
		int i = 0, j = n - 1;
		int mid = 0;

		if (k < a[0])
			return 0;
		else if (k >= a[n - 1])
			return n;
		else {
			while (j - i > 1) {
				mid = (i + j) / 2;

				if (k >= a[mid])
					i = mid;
				else
					j = mid;
			}
		}

		return i + 1;
	}

	public static long GCD(long a, long b) {
		if (b == 0)
			return a;
		else
			return GCD(b, a % b);
	}

	public static long LCM(long a, long b) {
		return (a * b) / GCD(a, b);
	}

	static class InputReader {

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

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

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

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

		public long nextLong() {
			int c = snext();
			while (isSpaceChar(c)) {
				c = snext();
			}
			int sgn = 1;
			if (c == '-') {
				sgn = -1;
				c = snext();
			}
			long res = 0;
			do {
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = snext();
			} 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 = snext();
			while (isSpaceChar(c)) {
				c = snext();
			}
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = snext();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = snext();
			while (isSpaceChar(c))
				c = snext();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = snext();
			} 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);
		}
	}

	static class CodeX {
		public static void sort(long arr[]) {
			merge_sort(arr, 0, arr.length - 1);
		}

		private static void merge_sort(long A[], long start, long end) {
			if (start < end) {
				long mid = (start + end) / 2;
				merge_sort(A, start, mid);
				merge_sort(A, mid + 1, end);
				merge(A, start, mid, end);
			}

		}

		private static void merge(long A[], long start, long mid, long end) {
			long p = start, q = mid + 1;
			long Arr[] = new long[(int) (end - start + 1)];
			long k = 0;

			for (int i = (int) start; i <= end; i++) {
				if (p > mid)
					Arr[(int) k++] = A[(int) q++];

				else if (q > end)
					Arr[(int) k++] = A[(int) p++];

				else if (A[(int) p] < A[(int) q])
					Arr[(int) k++] = A[(int) p++];

				else
					Arr[(int) k++] = A[(int) q++];
			}
			for (int i = 0; i < k; i++) {
				A[(int) start++] = Arr[i];
			}

		}

	}

}


import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.Set;
import java.util.Stack;

public class Solution {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		InputReader s = new InputReader(System.in);
		PrintWriter p = new PrintWriter(System.out);
		int ans = 0;
		int n = s.nextInt();
		int l = s.nextInt();
		HashMap<Integer, Integer>[] lang = new HashMap[n];
		int flag = 0;
		for (int i = 0; i < n; i++) {
			lang[i] = new HashMap<>();
			int m = s.nextInt();
			if (m != 0) {
				flag = 1;
			}
			for (int j = 0; j < m; j++) {
				lang[i].put(s.nextInt(), 1);
			}
		}
		if (flag == 0) {
			System.out.println(n);
			return;
		}
		int[] vis = new int[n];
		for (int i = 0; i < n; i++) {
			if (vis[i] == 0) {
				ans++;
				dfs(lang, vis, i);
			}
		}
		System.out.println(ans - 1);
		p.flush();
		p.close();
	}

	public static void dfs(HashMap<Integer, Integer>[] lang, int[] vis, int curr) {
		if (vis[curr] == 1) {
			return;
		}
		vis[curr] = 1;
		Set<Integer> lan = lang[curr].keySet();
		for (int i = 0; i < lang.length; i++) {
			for (int temp : lan) {
				if (lang[i].containsKey(temp)) {
					dfs(lang, vis, i);
				}
			}
		}
	}

	public static void union(int[] arr, int[] size, int a, int b) {
		int root_a = root(arr, a);
		int root_b = root(arr, b);
		if (size[root_a] >= size[root_b]) {
			arr[root_b] = root_a;
			size[root_a] += size[root_b];
		} else {
			arr[root_a] = root_b;
			size[root_b] += size[root_a];
		}
	}

	public static int root(int[] arr, int i) {
		while (arr[i] != i) {
			i = arr[i];
		}
		return i;
	}

	public static long power(long n, long x) {
		if (x == 0) {
			return 1;
		}
		long half = power(n, x / 2);
		if (x % 2 == 1) {
			return half * half * n;
		} else {
			return half * half;
		}
	}

	public static boolean check(ArrayList[] graph, int curr, int[] vis) {
		Stack<Integer> st = new Stack<>();
		long vert = 0;
		long edge = 0;
		vert = 1;
		st.push(curr);
		while (!st.isEmpty()) {
			int temp = st.pop();
			vis[temp] = 1;
			ArrayList<Integer> nbrs = graph[temp];
			edge += nbrs.size();
			for (int i = 0; i < nbrs.size(); i++) {
				int nbr = nbrs.get(i);
				if (vis[nbr] == 0) {
					st.push(nbr);
					vis[nbr] = 1;
					vert++;
				}
			}
		}
		if (edge == (vert * (vert - 1))) {
			return true;
		} else {
			return false;
		}
	}

	public static class node {
		int index;
		HashMap<Integer, Integer> nbrs;

		node(int i) {
			this.index = i;
			this.nbrs = new HashMap<>();
		}
	}

	static class pair implements Comparable<pair> {
		Integer x, y;

		pair(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public int compareTo(pair o) {
			int result = x.compareTo(o.x);
			if (result == 0)
				result = y.compareTo(o.y);

			return result;
		}

		public String toString() {
			return x + " " + y;
		}

		public boolean equals(Object o) {
			if (o instanceof pair) {
				pair p = (pair) o;
				return p.x - x == 0 && p.y - y == 0;
			}
			return false;
		}

		public int hashCode() {
			return new Long(x).hashCode() * 31 + new Long(y).hashCode();
		}
	}

	public static ArrayList Divisors(long n) {
		ArrayList<Long> div = new ArrayList<>();

		for (long i = 1; i <= Math.sqrt(n); i++) {
			if (n % i == 0) {
				div.add(i);

				if (n / i != i)
					div.add(n / i);
			}
		}
		return div;
	}

	public static int BinarySearch(long[] a, long k) {
		int n = a.length;
		int i = 0, j = n - 1;
		int mid = 0;

		if (k < a[0])
			return 0;
		else if (k >= a[n - 1])
			return n;
		else {
			while (j - i > 1) {
				mid = (i + j) / 2;

				if (k >= a[mid])
					i = mid;
				else
					j = mid;
			}
		}

		return i + 1;
	}

	public static long GCD(long a, long b) {
		if (b == 0)
			return a;
		else
			return GCD(b, a % b);
	}

	public static long LCM(long a, long b) {
		return (a * b) / GCD(a, b);
	}

	static class InputReader {

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

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

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

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

		public long nextLong() {
			int c = snext();
			while (isSpaceChar(c)) {
				c = snext();
			}
			int sgn = 1;
			if (c == '-') {
				sgn = -1;
				c = snext();
			}
			long res = 0;
			do {
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = snext();
			} 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 = snext();
			while (isSpaceChar(c)) {
				c = snext();
			}
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = snext();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = snext();
			while (isSpaceChar(c))
				c = snext();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = snext();
			} 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);
		}
	}

	static class CodeX {
		public static void sort(long arr[]) {
			merge_sort(arr, 0, arr.length - 1);
		}

		private static void merge_sort(long A[], long start, long end) {
			if (start < end) {
				long mid = (start + end) / 2;
				merge_sort(A, start, mid);
				merge_sort(A, mid + 1, end);
				merge(A, start, mid, end);
			}

		}

		private static void merge(long A[], long start, long mid, long end) {
			long p = start, q = mid + 1;
			long Arr[] = new long[(int) (end - start + 1)];
			long k = 0;

			for (int i = (int) start; i <= end; i++) {
				if (p > mid)
					Arr[(int) k++] = A[(int) q++];

				else if (q > end)
					Arr[(int) k++] = A[(int) p++];

				else if (A[(int) p] < A[(int) q])
					Arr[(int) k++] = A[(int) p++];

				else
					Arr[(int) k++] = A[(int) q++];
			}
			for (int i = 0; i < k; i++) {
				A[(int) start++] = Arr[i];
			}

		}

	}

}


import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.Set;
import java.util.Stack;

public class Solution {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		InputReader s = new InputReader(System.in);
		PrintWriter p = new PrintWriter(System.out);
		int ans = 0;
		int n = s.nextInt();
		int l = s.nextInt();
		HashMap<Integer, Integer>[] lang = new HashMap[n];
		int flag = 0;
		for (int i = 0; i < n; i++) {
			lang[i] = new HashMap<>();
			int m = s.nextInt();
			if (m != 0) {
				flag = 1;
			}
			for (int j = 0; j < m; j++) {
				lang[i].put(s.nextInt(), 1);
			}
		}
		if (flag == 0) {
			System.out.println(n);
			return;
		}
		ArrayList<pair> edges = new ArrayList<>();
		for (int i = 0; i < n; i++) {
			HashMap<Integer, Integer> temp = lang[i];
			Set<Integer> tempset = temp.keySet();
			for (int k : tempset) {
				for (int j = i + 1; j < n; j++) {
					if (lang[j].containsKey(k)) {
						edges.add(new pair(i, j));
						edges.add(new pair(j, i));
					}
				}
			}
		}
		int[] arr = new int[n];
		int[] size = new int[n];
		for (int i = 0; i < n; i++) {
			arr[i] = i;
			size[i] = 1;
		}
		for (int i = 0; i < edges.size(); i++) {
			union(arr, size, edges.get(i).x, edges.get(i).y);
		}
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] == i) {
				ans++;
			}
		}
		System.out.println(ans - 1);
		p.flush();
		p.close();
	}

	public static void union(int[] arr, int[] size, int a, int b) {
		int root_a = root(arr, a);
		int root_b = root(arr, b);
		if (size[root_a] >= size[root_b]) {
			arr[root_b] = root_a;
			size[root_a] += size[root_b];
		} else {
			arr[root_a] = root_b;
			size[root_b] += size[root_a];
		}
	}

	public static int root(int[] arr, int i) {
		while (arr[i] != i) {
			i = arr[i];
		}
		return i;
	}

	public static long power(long n, long x) {
		if (x == 0) {
			return 1;
		}
		long half = power(n, x / 2);
		if (x % 2 == 1) {
			return half * half * n;
		} else {
			return half * half;
		}
	}

	public static boolean check(ArrayList[] graph, int curr, int[] vis) {
		Stack<Integer> st = new Stack<>();
		long vert = 0;
		long edge = 0;
		vert = 1;
		st.push(curr);
		while (!st.isEmpty()) {
			int temp = st.pop();
			vis[temp] = 1;
			ArrayList<Integer> nbrs = graph[temp];
			edge += nbrs.size();
			for (int i = 0; i < nbrs.size(); i++) {
				int nbr = nbrs.get(i);
				if (vis[nbr] == 0) {
					st.push(nbr);
					vis[nbr] = 1;
					vert++;
				}
			}
		}
		if (edge == (vert * (vert - 1))) {
			return true;
		} else {
			return false;
		}
	}

	public static class node {
		int index;
		HashMap<Integer, Integer> nbrs;

		node(int i) {
			this.index = i;
			this.nbrs = new HashMap<>();
		}
	}

	static class pair implements Comparable<pair> {
		Integer x, y;

		pair(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public int compareTo(pair o) {
			int result = x.compareTo(o.x);
			if (result == 0)
				result = y.compareTo(o.y);

			return result;
		}

		public String toString() {
			return x + " " + y;
		}

		public boolean equals(Object o) {
			if (o instanceof pair) {
				pair p = (pair) o;
				return p.x - x == 0 && p.y - y == 0;
			}
			return false;
		}

		public int hashCode() {
			return new Long(x).hashCode() * 31 + new Long(y).hashCode();
		}
	}

	public static ArrayList Divisors(long n) {
		ArrayList<Long> div = new ArrayList<>();

		for (long i = 1; i <= Math.sqrt(n); i++) {
			if (n % i == 0) {
				div.add(i);

				if (n / i != i)
					div.add(n / i);
			}
		}
		return div;
	}

	public static int BinarySearch(long[] a, long k) {
		int n = a.length;
		int i = 0, j = n - 1;
		int mid = 0;

		if (k < a[0])
			return 0;
		else if (k >= a[n - 1])
			return n;
		else {
			while (j - i > 1) {
				mid = (i + j) / 2;

				if (k >= a[mid])
					i = mid;
				else
					j = mid;
			}
		}

		return i + 1;
	}

	public static long GCD(long a, long b) {
		if (b == 0)
			return a;
		else
			return GCD(b, a % b);
	}

	public static long LCM(long a, long b) {
		return (a * b) / GCD(a, b);
	}

	static class InputReader {

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

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

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

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

		public long nextLong() {
			int c = snext();
			while (isSpaceChar(c)) {
				c = snext();
			}
			int sgn = 1;
			if (c == '-') {
				sgn = -1;
				c = snext();
			}
			long res = 0;
			do {
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res *= 10;
				res += c - '0';
				c = snext();
			} 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 = snext();
			while (isSpaceChar(c)) {
				c = snext();
			}
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = snext();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = snext();
			while (isSpaceChar(c))
				c = snext();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = snext();
			} 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);
		}
	}

	static class CodeX {
		public static void sort(long arr[]) {
			merge_sort(arr, 0, arr.length - 1);
		}

		private static void merge_sort(long A[], long start, long end) {
			if (start < end) {
				long mid = (start + end) / 2;
				merge_sort(A, start, mid);
				merge_sort(A, mid + 1, end);
				merge(A, start, mid, end);
			}

		}

		private static void merge(long A[], long start, long mid, long end) {
			long p = start, q = mid + 1;
			long Arr[] = new long[(int) (end - start + 1)];
			long k = 0;

			for (int i = (int) start; i <= end; i++) {
				if (p > mid)
					Arr[(int) k++] = A[(int) q++];

				else if (q > end)
					Arr[(int) k++] = A[(int) p++];

				else if (A[(int) p] < A[(int) q])
					Arr[(int) k++] = A[(int) p++];

				else
					Arr[(int) k++] = A[(int) q++];
			}
			for (int i = 0; i < k; i++) {
				A[(int) start++] = Arr[i];
			}

		}

	}

}


import java.util.*;
import java.io.*;
import java.math.BigInteger;

public class A277{
    static int gcd(int a, int b)
    {
        if (a == 0)
            return b;
        if (b == 0)
            return a;
        int k;
        for (k = 0; ((a | b) & 1) == 0; ++k)
        {
            a >>= 1;
            b >>= 1;
        }
        while ((a & 1) == 0)
            a >>= 1;

        do {
            while ((b & 1) == 0)
                b >>= 1;
            if (a > b)
            {
                int temp = a;
                a = b;
                b = temp;
            }

            b = (b - a);
        } while (b != 0);
        return a << k;
    }
    static int lcm(int a,int b){
        return a*b/gcd(a,b);
    }
	public static int binary(int arr[],int n, int a,int beg,int end){
		while(beg<=end){
			int mid=beg+(end-beg)/2;
			if(arr[mid]==a)
				return mid;
			if(arr[mid]<a)
				end=mid-1;
			else
				beg=mid+1;
		}
		return -1;
	}
	public static void sieve(int t){
		boolean srr[]=new boolean[t+1];

        for(int i=2;i*i<=t;i++)
        {
            if(!srr[i])
            {
                for(int k=i*i;k<=t;k+=i)
                    srr[k]=true;
            }
        }
        for(int i=2;i<t+1;i++){
        	if(!srr[i])
            	System.out.println(i);
        }
	}
    static int power(int x, int y, int p)
    {
        int res = 1;
        x = x % p;
        while (y > 0)
        {
            if((y & 1)==1)
                res = (res * x) % p;
            y = y >> 1;
            x = (x * x) % p;
        }
        return res;
    }

    static class Custom implements Comparable<Custom>{
        int d;
        int f;
        public Custom(int d,int f){
            this.d=d;
            this.f=f;
        }
        public int compareTo(Custom t){
            return this.d-t.d;
        }
    }
    void solve() throws IOException {
        int n=nextInt();
        int m=nextInt();
        ArrayList<HashSet<Integer>> adj=new ArrayList<>();
        for(int i=0;i<m;i++)
            adj.add(new HashSet<>());

        int ans=0;
        for(int i=0;i<n;i++){
            int k=nextInt();
            int x=0;
            if(k!=0){
                k--;
                 x=nextInt()-1;
                 adj.get(x).add(x);
            }
            else{ans++;}
            while(k--!=0){
                int y=nextInt()-1;
                adj.get(x).add(y);
                adj.get(y).add(x);
            }
        }
        boolean set=true;
        HashSet<Integer> visited=new HashSet<>();
        for(int i=0;i<n;i++){
            if(!visited.contains(i)&&adj.get(i).size()>0){
                // System.out.println(i+"FDD");
                if(set){
                    ans--;
                    set=false;
                }
                ans++;
                Queue<Integer> qu=new LinkedList<>();
                qu.add(i);
                while(qu.size()>0){
                    int p=qu.poll();
                    visited.add(p);
                    for(int j:adj.get(p)){
                        if(!visited.contains(j)){
                            qu.add(j);
                        }
                    }
                }
            }
        }
        pw.println(ans);

    }
	String next() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            st = new StringTokenizer(br.readLine());
        }
        return st.nextToken();
    }
    String nextLine()throws IOException{
        return br.readLine();
    }
    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }
    long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    StringTokenizer st;
    BufferedReader br;
    PrintWriter pw;
    Scanner sc;
    void run() throws IOException {
        sc=new Scanner(new BufferedReader(new InputStreamReader(System.in)));
        br = new BufferedReader(new InputStreamReader(System.in));
        pw = new PrintWriter(System.out);
        long time = System.currentTimeMillis();
        solve();
        System.err.println("time = " + (System.currentTimeMillis() - time));
        pw.close();
    }

    public static void main(String[] args) throws IOException {
        new A277().run();
    }

}


import java.util.*;
import java.io.*;
import java.math.BigInteger;

public class A277{
    static int gcd(int a, int b)
    {
        if (a == 0)
            return b;
        if (b == 0)
            return a;
        int k;
        for (k = 0; ((a | b) & 1) == 0; ++k)
        {
            a >>= 1;
            b >>= 1;
        }
        while ((a & 1) == 0)
            a >>= 1;

        do {
            while ((b & 1) == 0)
                b >>= 1;
            if (a > b)
            {
                int temp = a;
                a = b;
                b = temp;
            }

            b = (b - a);
        } while (b != 0);
        return a << k;
    }
    static int lcm(int a,int b){
        return a*b/gcd(a,b);
    }
	public static int binary(int arr[],int n, int a,int beg,int end){
		while(beg<=end){
			int mid=beg+(end-beg)/2;
			if(arr[mid]==a)
				return mid;
			if(arr[mid]<a)
				end=mid-1;
			else
				beg=mid+1;
		}
		return -1;
	}
	public static void sieve(int t){
		boolean srr[]=new boolean[t+1];

        for(int i=2;i*i<=t;i++)
        {
            if(!srr[i])
            {
                for(int k=i*i;k<=t;k+=i)
                    srr[k]=true;
            }
        }
        for(int i=2;i<t+1;i++){
        	if(!srr[i])
            	System.out.println(i);
        }
	}
    static int power(int x, int y, int p)
    {
        int res = 1;
        x = x % p;
        while (y > 0)
        {
            if((y & 1)==1)
                res = (res * x) % p;
            y = y >> 1;
            x = (x * x) % p;
        }
        return res;
    }

    static class Custom implements Comparable<Custom>{
        int d;
        int f;
        public Custom(int d,int f){
            this.d=d;
            this.f=f;
        }
        public int compareTo(Custom t){
            return this.d-t.d;
        }
    }
    void solve() throws IOException {
        int n=nextInt();
        int m=nextInt();
        ArrayList<HashSet<Integer>> adj=new ArrayList<>();
        for(int i=0;i<n;i++)
            adj.add(new HashSet<>());

        int ans=-1;
        for(int i=0;i<n;i++){
            int k=nextInt();
            int x=0;
            if(k!=0){
                k--;
                 x=nextInt()-1;
                 adj.get(x).add(x);
            }
            else{ans++;}
            while(k--!=0){
                int y=nextInt()-1;
                adj.get(x).add(y);
                adj.get(y).add(x);
            }
        }
        HashSet<Integer> visited=new HashSet<>();
        for(int i=0;i<n;i++){
            if(!visited.contains(i)&&adj.get(i).size()>0){
                System.out.println(i+"FDD");
                ans++;
                Queue<Integer> qu=new LinkedList<>();
                qu.add(i);
                while(qu.size()>0){
                    int p=qu.poll();
                    visited.add(p);
                    for(int j:adj.get(p)){
                        if(!visited.contains(j)){
                            qu.add(j);
                        }
                    }
                }
            }
        }
        pw.println(ans);

    }
	String next() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            st = new StringTokenizer(br.readLine());
        }
        return st.nextToken();
    }
    String nextLine()throws IOException{
        return br.readLine();
    }
    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }
    long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    StringTokenizer st;
    BufferedReader br;
    PrintWriter pw;
    Scanner sc;
    void run() throws IOException {
        sc=new Scanner(new BufferedReader(new InputStreamReader(System.in)));
        br = new BufferedReader(new InputStreamReader(System.in));
        pw = new PrintWriter(System.out);
        long time = System.currentTimeMillis();
        solve();
        System.err.println("time = " + (System.currentTimeMillis() - time));
        pw.close();
    }

    public static void main(String[] args) throws IOException {
        new A277().run();
    }

}


import java.util.*;
import java.io.*;
import java.math.BigInteger;

public class A277{
    static int gcd(int a, int b)
    {
        if (a == 0)
            return b;
        if (b == 0)
            return a;
        int k;
        for (k = 0; ((a | b) & 1) == 0; ++k)
        {
            a >>= 1;
            b >>= 1;
        }
        while ((a & 1) == 0)
            a >>= 1;

        do {
            while ((b & 1) == 0)
                b >>= 1;
            if (a > b)
            {
                int temp = a;
                a = b;
                b = temp;
            }

            b = (b - a);
        } while (b != 0);
        return a << k;
    }
    static int lcm(int a,int b){
        return a*b/gcd(a,b);
    }
	public static int binary(int arr[],int n, int a,int beg,int end){
		while(beg<=end){
			int mid=beg+(end-beg)/2;
			if(arr[mid]==a)
				return mid;
			if(arr[mid]<a)
				end=mid-1;
			else
				beg=mid+1;
		}
		return -1;
	}
	public static void sieve(int t){
		boolean srr[]=new boolean[t+1];

        for(int i=2;i*i<=t;i++)
        {
            if(!srr[i])
            {
                for(int k=i*i;k<=t;k+=i)
                    srr[k]=true;
            }
        }
        for(int i=2;i<t+1;i++){
        	if(!srr[i])
            	System.out.println(i);
        }
	}
    static int power(int x, int y, int p)
    {
        int res = 1;
        x = x % p;
        while (y > 0)
        {
            if((y & 1)==1)
                res = (res * x) % p;
            y = y >> 1;
            x = (x * x) % p;
        }
        return res;
    }

    static class Custom implements Comparable<Custom>{
        int d;
        int f;
        public Custom(int d,int f){
            this.d=d;
            this.f=f;
        }
        public int compareTo(Custom t){
            return this.d-t.d;
        }
    }
    void solve() throws IOException {
        int n=nextInt();
        int m=nextInt();
        ArrayList<HashSet<Integer>> adj=new ArrayList<>();
        for(int i=0;i<n;i++)
            adj.add(new HashSet<>());

        int ans=-1;
        for(int i=0;i<n;i++){
            int k=nextInt();
            int x=0;
            if(k!=0){
                k--;
                 x=nextInt()-1;
                 adj.get(x).add(x);
            }
            else{ans++;}
            while(k--!=0){
                int y=nextInt()-1;
                adj.get(x).add(y);
                adj.get(y).add(x);
            }
        }
        HashSet<Integer> visited=new HashSet<>();
        for(int i=0;i<n;i++){
            if(!visited.contains(i)&&adj.get(i).size()>0){
                // System.out.println(i+"FDD");
                ans++;
                Queue<Integer> qu=new LinkedList<>();
                qu.add(i);
                while(qu.size()>0){
                    int p=qu.poll();
                    visited.add(p);
                    for(int j:adj.get(p)){
                        if(!visited.contains(j)){
                            qu.add(j);
                        }
                    }
                }
            }
        }
        pw.println(ans);

    }
	String next() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            st = new StringTokenizer(br.readLine());
        }
        return st.nextToken();
    }
    String nextLine()throws IOException{
        return br.readLine();
    }
    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }
    long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    StringTokenizer st;
    BufferedReader br;
    PrintWriter pw;
    Scanner sc;
    void run() throws IOException {
        sc=new Scanner(new BufferedReader(new InputStreamReader(System.in)));
        br = new BufferedReader(new InputStreamReader(System.in));
        pw = new PrintWriter(System.out);
        long time = System.currentTimeMillis();
        solve();
        System.err.println("time = " + (System.currentTimeMillis() - time));
        pw.close();
    }

    public static void main(String[] args) throws IOException {
        new A277().run();
    }

}


import java.util.*;
import java.io.*;
import java.math.BigInteger;

public class A277{
    static int gcd(int a, int b)
    {
        if (a == 0)
            return b;
        if (b == 0)
            return a;
        int k;
        for (k = 0; ((a | b) & 1) == 0; ++k)
        {
            a >>= 1;
            b >>= 1;
        }
        while ((a & 1) == 0)
            a >>= 1;

        do {
            while ((b & 1) == 0)
                b >>= 1;
            if (a > b)
            {
                int temp = a;
                a = b;
                b = temp;
            }

            b = (b - a);
        } while (b != 0);
        return a << k;
    }
    static int lcm(int a,int b){
        return a*b/gcd(a,b);
    }
	public static int binary(int arr[],int n, int a,int beg,int end){
		while(beg<=end){
			int mid=beg+(end-beg)/2;
			if(arr[mid]==a)
				return mid;
			if(arr[mid]<a)
				end=mid-1;
			else
				beg=mid+1;
		}
		return -1;
	}
	public static void sieve(int t){
		boolean srr[]=new boolean[t+1];

        for(int i=2;i*i<=t;i++)
        {
            if(!srr[i])
            {
                for(int k=i*i;k<=t;k+=i)
                    srr[k]=true;
            }
        }
        for(int i=2;i<t+1;i++){
        	if(!srr[i])
            	System.out.println(i);
        }
	}
    static int power(int x, int y, int p)
    {
        int res = 1;
        x = x % p;
        while (y > 0)
        {
            if((y & 1)==1)
                res = (res * x) % p;
            y = y >> 1;
            x = (x * x) % p;
        }
        return res;
    }

    static class Custom implements Comparable<Custom>{
        int d;
        int f;
        public Custom(int d,int f){
            this.d=d;
            this.f=f;
        }
        public int compareTo(Custom t){
            return this.d-t.d;
        }
    }
    void solve() throws IOException {
        int n=nextInt();
        int m=nextInt();
        ArrayList<HashSet<Integer>> adj=new ArrayList<>();
        for(int i=0;i<n;i++)
            adj.add(new HashSet<>());

        int ans=0;
        for(int i=0;i<n;i++){
            int k=nextInt();
            int x=0;
            if(k!=0){
                k--;
                 x=nextInt()-1;
                 adj.get(x).add(x);
            }
            else{ans++;}
            while(k--!=0){
                int y=nextInt()-1;
                adj.get(x).add(y);
                adj.get(y).add(x);
            }
        }
        boolean set=true;
        HashSet<Integer> visited=new HashSet<>();
        for(int i=0;i<n;i++){
            if(!visited.contains(i)&&adj.get(i).size()>0){
                // System.out.println(i+"FDD");
                if(set){
                    ans--;
                    set=false;
                }
                ans++;
                Queue<Integer> qu=new LinkedList<>();
                qu.add(i);
                while(qu.size()>0){
                    int p=qu.poll();
                    visited.add(p);
                    for(int j:adj.get(p)){
                        if(!visited.contains(j)){
                            qu.add(j);
                        }
                    }
                }
            }
        }
        pw.println(ans);

    }
	String next() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            st = new StringTokenizer(br.readLine());
        }
        return st.nextToken();
    }
    String nextLine()throws IOException{
        return br.readLine();
    }
    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }
    long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    StringTokenizer st;
    BufferedReader br;
    PrintWriter pw;
    Scanner sc;
    void run() throws IOException {
        sc=new Scanner(new BufferedReader(new InputStreamReader(System.in)));
        br = new BufferedReader(new InputStreamReader(System.in));
        pw = new PrintWriter(System.out);
        long time = System.currentTimeMillis();
        solve();
        System.err.println("time = " + (System.currentTimeMillis() - time));
        pw.close();
    }

    public static void main(String[] args) throws IOException {
        new A277().run();
    }

}


import java.util.*;
import java.io.*;
import java.math.BigInteger;

public class A277{
    static int gcd(int a, int b)
    {
        if (a == 0)
            return b;
        if (b == 0)
            return a;
        int k;
        for (k = 0; ((a | b) & 1) == 0; ++k)
        {
            a >>= 1;
            b >>= 1;
        }
        while ((a & 1) == 0)
            a >>= 1;

        do {
            while ((b & 1) == 0)
                b >>= 1;
            if (a > b)
            {
                int temp = a;
                a = b;
                b = temp;
            }

            b = (b - a);
        } while (b != 0);
        return a << k;
    }
    static int lcm(int a,int b){
        return a*b/gcd(a,b);
    }
	public static int binary(int arr[],int n, int a,int beg,int end){
		while(beg<=end){
			int mid=beg+(end-beg)/2;
			if(arr[mid]==a)
				return mid;
			if(arr[mid]<a)
				end=mid-1;
			else
				beg=mid+1;
		}
		return -1;
	}
	public static void sieve(int t){
		boolean srr[]=new boolean[t+1];

        for(int i=2;i*i<=t;i++)
        {
            if(!srr[i])
            {
                for(int k=i*i;k<=t;k+=i)
                    srr[k]=true;
            }
        }
        for(int i=2;i<t+1;i++){
        	if(!srr[i])
            	System.out.println(i);
        }
	}
    static int power(int x, int y, int p)
    {
        int res = 1;
        x = x % p;
        while (y > 0)
        {
            if((y & 1)==1)
                res = (res * x) % p;
            y = y >> 1;
            x = (x * x) % p;
        }
        return res;
    }

    static class Custom implements Comparable<Custom>{
        int d;
        int f;
        public Custom(int d,int f){
            this.d=d;
            this.f=f;
        }
        public int compareTo(Custom t){
            return this.d-t.d;
        }
    }
    void solve() throws IOException {
        int n=nextInt();
        int m=nextInt();
        ArrayList<HashSet<Integer>> adj=new ArrayList<>();
        for(int i=0;i<m;i++)
            adj.add(new HashSet<>());

        int ans=0;
        for(int i=0;i<n;i++){
            int k=nextInt();
            int x=0;
            if(k!=0){
                k--;
                 x=nextInt()-1;
                 adj.get(x).add(x);
            }
            else{ans++;}
            while(k--!=0){
                int y=nextInt()-1;
                adj.get(x).add(y);
                adj.get(y).add(x);
            }
        }
        boolean set=true;
        HashSet<Integer> visited=new HashSet<>();
        for(int i=0;i<m;i++){
            if(!visited.contains(i)&&adj.get(i).size()>0){
                // System.out.println(i+"FDD");
                if(set){
                    ans--;
                    set=false;
                }
                ans++;
                Queue<Integer> qu=new LinkedList<>();
                qu.add(i);
                while(qu.size()>0){
                    int p=qu.poll();
                    visited.add(p);
                    for(int j:adj.get(p)){
                        if(!visited.contains(j)){
                            qu.add(j);
                        }
                    }
                }
            }
        }
        pw.println(ans);

    }
	String next() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            st = new StringTokenizer(br.readLine());
        }
        return st.nextToken();
    }
    String nextLine()throws IOException{
        return br.readLine();
    }
    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }
    long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    StringTokenizer st;
    BufferedReader br;
    PrintWriter pw;
    Scanner sc;
    void run() throws IOException {
        sc=new Scanner(new BufferedReader(new InputStreamReader(System.in)));
        br = new BufferedReader(new InputStreamReader(System.in));
        pw = new PrintWriter(System.out);
        long time = System.currentTimeMillis();
        solve();
        System.err.println("time = " + (System.currentTimeMillis() - time));
        pw.close();
    }

    public static void main(String[] args) throws IOException {
        new A277().run();
    }

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.StringTokenizer;

public class force1 {
	static PrintWriter pw = new PrintWriter(System.out);
	static HashSet<Integer> h;
	static HashSet<Integer> p;
	static void asd(int n,int c,int[] x) {
		//System.out.println(Arrays.toString(x));
		if(c==n-1) {
			if(isprime(x[0]+x[n-1]))
			print(x);
			return;
		}
		for(int i=2;i<=n;i++) {
			if(!h.contains(i) && isprime(x[c]+i)) {
				h.add(i);
				x[c+1]=i;
				asd(n,c+1,x);
				h.remove(i);
			}
		}
	}

	static boolean isprime(int n) {
		return p.contains(n);
	}

	static void print (int[] x) {
		for(int i=0;i<x.length;i++) {
			if(i==x.length-1)
				pw.print(x[i]);
			else
				pw.print(x[i]+" ");
		}
		pw.println();
	}

	static boolean [] vis;
	static boolean [] t;
	static ArrayList[] a2 ;
	static int c;
	static int c2;
	static void dfs(ArrayList[] a,int s) {
		for(Object x:a[s]) {
			if(!vis[(int)x]) {
				vis[(int)x]=true;
				dfs(a,(int)x);
			}
		}
	}
	static void helper(ArrayList[] a) {
		for(int i=0;i<vis.length;i++) {
			if(!vis[i]) {
				vis[i]=true;
				if(a2[i]!=null) {
					dfs(a,i);
					c++;
				}
				else
					c2++;
			}
		}
	}
	public static void main(String[] args) throws NumberFormatException, IOException, InterruptedException {
		BufferedReader  br= new BufferedReader(new InputStreamReader( System.in));
		pw = new PrintWriter(System.out);
		Scanner sc =new Scanner(System .in);
		int n=sc.nextInt();
		int l=sc.nextInt();
		ArrayList[] a=new ArrayList[n];
		a2 = new ArrayList[n];
		ArrayList[] al = new ArrayList[n];
		for(int i=0;i<n;i++) {
			int k=sc.nextInt();
			for(int j=0;j<k;j++) {
				int x=sc.nextInt();
				if(al[x-1]==null)
					al[x-1]=new ArrayList<>();
				al[x-1].add(i);
				if(a2[i]==null)
					a2[i]=new ArrayList<>();
				a2[i].add(x-1);
			}
		}
//		System.out.println(al[2]);
		for(int i=0;i<n;i++) {
			a[i] = new ArrayList<>();
			if(a2[i]!=null) {
				for(Object x:a2[i]) {
					for(Object y:al[(int)x]) {
						if(!a[i].contains(y) && (int)y!=i)
							a[i].add(y);
					}
				}
			}

		}
//		System.out.println(a[2]);
		vis=new boolean[n];
		c=0;
		helper(a);
		if(c==0)
			System.out.println(c2);
		else
			System.out.println(c2+c-1);
		pw.flush();
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.StringTokenizer;

public class force1 {
	static PrintWriter pw = new PrintWriter(System.out);
	static HashSet<Integer> h;
	static HashSet<Integer> p;
	static void asd(int n,int c,int[] x) {
		//System.out.println(Arrays.toString(x));
		if(c==n-1) {
			if(isprime(x[0]+x[n-1]))
			print(x);
			return;
		}
		for(int i=2;i<=n;i++) {
			if(!h.contains(i) && isprime(x[c]+i)) {
				h.add(i);
				x[c+1]=i;
				asd(n,c+1,x);
				h.remove(i);
			}
		}
	}

	static boolean isprime(int n) {
		return p.contains(n);
	}

	static void print (int[] x) {
		for(int i=0;i<x.length;i++) {
			if(i==x.length-1)
				pw.print(x[i]);
			else
				pw.print(x[i]+" ");
		}
		pw.println();
	}

	static boolean [] vis;
	static boolean [] t;
	static int c;
	static void dfs(ArrayList[] a,int s) {
		for(Object x:a[s]) {
			if(!vis[(int)x]) {
				vis[(int)x]=true;
				dfs(a,(int)x);
			}
		}
	}
	static void helper(ArrayList[] a) {
		for(int i=0;i<vis.length;i++) {
			if(!vis[i]) {
				c++;
				vis[i]=true;
				if(a[i]!=null)
					dfs(a,i);
			}
		}
	}
	public static void main(String[] args) throws NumberFormatException, IOException, InterruptedException {
		BufferedReader  br= new BufferedReader(new InputStreamReader( System.in));
		pw = new PrintWriter(System.out);
		Scanner sc =new Scanner(System .in);
		int n=sc.nextInt();
		int l=sc.nextInt();
		ArrayList[] a=new ArrayList[n];
		ArrayList[] a2 = new ArrayList[n];
		ArrayList[] al = new ArrayList[n];
		for(int i=0;i<n;i++) {
			int k=sc.nextInt();
			for(int j=0;j<k;j++) {
				int x=sc.nextInt();
				if(al[x-1]==null)
					al[x-1]=new ArrayList<>();
				al[x-1].add(i);
				if(a2[i]==null)
					a2[i]=new ArrayList<>();
				a2[i].add(x-1);
			}
		}
//		System.out.println(al[2]);
		for(int i=0;i<n;i++) {
			a[i] = new ArrayList<>();
			if(a2[i]!=null) {
				for(Object x:a2[i]) {
					for(Object y:al[(int)x]) {
						if(!a[i].contains(y) && (int)y!=i)
							a[i].add(y);
					}
				}
			}

		}
//		System.out.println(a[2]);
		vis=new boolean[n];
		c=0;
		helper(a);
		System.out.println(c-1);
		pw.flush();
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.StringTokenizer;

public class force1 {
	static PrintWriter pw = new PrintWriter(System.out);
	static HashSet<Integer> h;
	static HashSet<Integer> p;
	static void asd(int n,int c,int[] x) {
		//System.out.println(Arrays.toString(x));
		if(c==n-1) {
			if(isprime(x[0]+x[n-1]))
			print(x);
			return;
		}
		for(int i=2;i<=n;i++) {

			if(!h.contains(i) && isprime(x[c]+i)) {
				h.add(i);
				x[c+1]=i;
				asd(n,c+1,x);
				h.remove(i);
			}
		}
	}

	static boolean isprime(int n) {
		return p.contains(n);
	}

	static void print (int[] x) {
		for(int i=0;i<x.length;i++) {
			if(i==x.length-1)
				pw.print(x[i]);
			else
				pw.print(x[i]+" ");
		}
		pw.println();
	}

	static boolean [] vis;
	static boolean [] t;
	static ArrayList[] a2 ;
	static int c;
	static int c2;
	static void dfs(ArrayList[] a,int s) {
		for(Object x:a[s]) {
			if(!vis[(int)x]) {
				vis[(int)x]=true;
				dfs(a,(int)x);
			}
		}
	}
	static void helper(ArrayList[] a) {
		for(int i=0;i<vis.length;i++) {
			if(!vis[i]) {
				vis[i]=true;
				if(a2[i]!=null) {
					dfs(a,i);
					c++;
				}
				else
					c2++;
			}
		}
	}
	public static void main(String[] args) throws NumberFormatException, IOException, InterruptedException {
		BufferedReader  br= new BufferedReader(new InputStreamReader( System.in));
		pw = new PrintWriter(System.out);
		Scanner sc =new Scanner(System .in);
		int n=sc.nextInt();
		int l=sc.nextInt();
		ArrayList[] a=new ArrayList[n];
		a2 = new ArrayList[n];
		ArrayList[] al = new ArrayList[l];
		for(int i=0;i<n;i++) {
			int k=sc.nextInt();
			for(int j=0;j<k;j++) {
				int x=sc.nextInt();
				if(al[x-1]==null)
					al[x-1]=new ArrayList<>();
				al[x-1].add(i);
				if(a2[i]==null)
					a2[i]=new ArrayList<>();
				a2[i].add(x-1);
			}
		}
//		System.out.println(al[2]);
		for(int i=0;i<n;i++) {
			a[i] = new ArrayList<>();
			if(a2[i]!=null) {
				for(Object x:a2[i]) {
					for(Object y:al[(int)x]) {
						if(!a[i].contains(y) && (int)y!=i)
							a[i].add(y);
					}
				}
			}

		}
//		System.out.println(a[2]);
		vis=new boolean[n];
		c=0;
		helper(a);
		if(c==0)
			System.out.println(c2);
		else
			System.out.println(c2+c-1);
		pw.flush();
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.StringTokenizer;

public class force1 {
	static PrintWriter pw = new PrintWriter(System.out);
	static HashSet<Integer> h;
	static HashSet<Integer> p;
	static void asd(int n,int c,int[] x) {
		//System.out.println(Arrays.toString(x));
		if(c==n-1) {
			if(isprime(x[0]+x[n-1]))
			print(x);
			return;
		}
		for(int i=2;i<=n;i++) {
			if(!h.contains(i) && isprime(x[c]+i)) {
				h.add(i);
				x[c+1]=i;
				asd(n,c+1,x);
				h.remove(i);
			}
		}
	}

	static boolean isprime(int n) {
		return p.contains(n);
	}

	static void print (int[] x) {
		for(int i=0;i<x.length;i++) {
			if(i==x.length-1)
				pw.print(x[i]);
			else
				pw.print(x[i]+" ");
		}
		pw.println();
	}

	static boolean [] vis;
	static boolean [] t;
	static ArrayList[] a2 ;
	static int c;
	static int c2;
	static void dfs(ArrayList[] a,int s) {
		for(Object x:a[s]) {
			if(!vis[(int)x]) {
				vis[(int)x]=true;
				dfs(a,(int)x);
			}
		}
	}
	static void helper(ArrayList[] a) {
		for(int i=0;i<vis.length;i++) {
			if(!vis[i]) {
				vis[i]=true;
				if(a2[i]!=null) {
					dfs(a,i);
					c++;
				}
				else
					c2++;
			}
		}
	}
	public static void main(String[] args) throws NumberFormatException, IOException, InterruptedException {
		BufferedReader  br= new BufferedReader(new InputStreamReader( System.in));
		pw = new PrintWriter(System.out);
		Scanner sc =new Scanner(System .in);
		int n=sc.nextInt();
		int l=sc.nextInt();
		ArrayList[] a=new ArrayList[n];
		a2 = new ArrayList[n];
		ArrayList[] al = new ArrayList[l];
		for(int i=0;i<n;i++) {
			int k=sc.nextInt();
			for(int j=0;j<k;j++) {
				int x=sc.nextInt();
				if(al[x-1]==null)
					al[x-1]=new ArrayList<>();
				al[x-1].add(i);
				if(a2[i]==null)
					a2[i]=new ArrayList<>();
				a2[i].add(x-1);
			}
		}
//		System.out.println(al[2]);
		for(int i=0;i<n;i++) {
			a[i] = new ArrayList<>();
			if(a2[i]!=null) {
				for(Object x:a2[i]) {
					for(Object y:al[(int)x]) {
						if(!a[i].contains(y) && (int)y!=i)
							a[i].add(y);
					}
				}
			}

		}
//		System.out.println(a[2]);
		vis=new boolean[n];
		c=0;
		helper(a);
		if(c==0)
			System.out.println(c2);
		else
			System.out.println(c2+c-1);
		pw.flush();
	}
}


import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;
public class LearningLanguagesA
{
    public static void main(String args[])
    {
        int cnt=0;
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        ArrayList<ArrayList<Integer>> ar=new ArrayList<>();
        ArrayList<Integer> sol[]=new ArrayList[m+1];
        for(int i=0;i<=m;i++)
        {
            sol[i]=new ArrayList<>();
        }
        for(int i=0;i<n;i++)
        {
            int ct=sc.nextInt();
            ArrayList<Integer> p=new ArrayList<>();
            for(int k=0;k<ct;k++)
            {
                p.add(sc.nextInt());
            }
            if(p.isEmpty())
            {
                cnt++;
            }
            else
            {
                ar.add(p);
            }
        }
        int arr[]=new int[m+1];
        int mt=0;int val;
        for(ArrayList<Integer> k : ar)
        {
            mt++;
            for(Integer m1: k)
            {
                sol[m1].add(mt);
            }
        }
        mt=0;
        for(ArrayList<Integer> k :sol)
        {
            val=0;
            if(k.size()>=1) {
                mt++;
                for (Integer m1 : k
                ) {
                    if (arr[m1] != 0) {
                        val = arr[m1];
                    }
                }
                if (val != 0) {
                    for (Integer m1 : k
                    ) {
                        arr[m1] = val;
                    }
                } else {
                    for (Integer m1 : k
                    ) {
                        arr[m1] = mt;
                    }
                }
            }
        }
        HashSet<Integer> hs=new HashSet<>();
        for(int i=1;i<=m;i++)
        {
            if(arr[i]!=0)
            hs.add(arr[i]);
        }
	if(hs.size()!=0)
        System.out.println(hs.size()+cnt-1);
	else
        {
            System.out.println(cnt);
        }
    }
}