#include <bits/stdc++.h>
using namespace std;
long long n, t, m, c1, c2, c, x, y, z, sum, mn = 999999999, avg, pos, dif, k,
                                            mx, xi, yi;
const int MXN = 1e5 + 7;
long long pre[MXN];
bool prime[MXN];
bool PE(long long x) {
  for (long long i = 2; i * i <= x; i += 1 + (i & 1))
    if (x % i == 0) return false;
  return true;
}
long long dist[MXN];
void sieve() {
  memset(prime, 1, sizeof prime);
  for (long long i = 2; i * i <= MXN; i += 1 + (i & 1))
    if (prime[i]) {
      for (long long j = i * i; j < MXN; j += i) prime[j] = false;
    }
}
bool vis[MXN], talked[MXN];
bool PR(string s) {
  int n = s.length();
  for (long long i = 0; i < n / 2; i++)
    if (s[i] != s[n - i - 1]) return false;
  return true;
}
long long dsu[MXN];
int FindRoot(int i) {
  if (dsu[i] == i) return i;
  return dsu[i] = FindRoot(dsu[i]);
}
int main(void) {
  ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  ;
  cin >> n >> m;
  for (int i = 1; i <= n; i++) dsu[i] = i;
  for (int i = 1; i <= n; i++) {
    int pa;
    cin >> z;
    if (!z) c++;
    for (int j = 1; j <= z; j++) {
      cin >> x;
      talked[x] = true;
      if (j == 1) {
        pa = FindRoot(x);
        continue;
      }
      int ch = FindRoot(x);
      dsu[ch] = pa;
    }
  }
  bool u = false;
  for (int i = 1; i <= m; i++) {
    if (!talked[i]) continue;
    u = true;
    int a = FindRoot(i);
    if (!vis[a]) {
      c++;
      vis[a] = 1;
    }
  }
  u ? cout << --c : cout << c;
}
