#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<utility>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const ll INF = mod * mod;
#define rep(i,n) for(int i=0;i<n;i++)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define all(x) (x).begin(),(x).end()
#define stop char nyaa;cin>>nyaa;

typedef pair<int, int> P;

#include<cstring>
struct edge {
	int to, cap, rev;
};
const int sz = 1 << 18;
vector<edge> G[sz];
bool used[sz];
void add_edge(int from, int to, int cap) {
	G[from].push_back(edge{ to,cap,(int)G[to].size() });
	G[to].push_back(edge{ from,0,(int)G[from].size() - 1 });
}
int dfs(int v, int t, int f) {
	if (v == t)return f;
	used[v] = true;
	rep(i, (int)G[v].size()) {
		edge &e = G[v][i];
		if (!used[e.to] && e.cap > 0) {
			int d = dfs(e.to, t, min(f, e.cap));
			if (d > 0) {
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}
int max_flow(int s, int t) {
	int flow = 0;
	for (;;) {
		memset(used, 0, sizeof(used));
		int f = dfs(s, t, mod);
		if (f == 0)return flow;
		flow += f;
	}
}

vector<int> loc[1 << 18];
bool exi[1 << 18];

bool setexi[300];

bool e[300][300];

void solve() {
	int n, m; cin >> n >> m;
	fill(exi, exi + n, true);
	vector<set<int>> v(m);
	rep(i, m) {
		int k; cin >> k;
		rep(j, k) {
			int x; cin >> x; x--;
			v[i].insert(x);
			loc[x].push_back(i);
		}
	}
	int ans = 0;
	rep(i, n) {
		if (!exi[i])continue;
		if (loc[i].size() == 0) {
			ans++; exi[i] = false;
		}
		else if (loc[i].size() == 1) {
			ans++;
			int id = loc[i][0];
			for (auto itr = v[id].begin(); itr != v[id].end(); itr++) {
				int z = *itr;
				exi[z] = false;
			}
		}
	}
	vector<int> aite(m);
	vector<bool> b(m, false);
	rep(i, n) {
		if (!exi[i])continue;
		int l = loc[i][0];
		int r = loc[i][1];
		e[l][r] = e[r][l] = true;
	}
	rep(i, m) {
		rep(j, m) {
			if (i == j)continue;
			if (!b[i] && !b[j] && e[i][j]) {
				b[i] = b[j] = true;
				aite[i] = j;
				aite[j] = i;
				//cout << i << " " << j << endl;
				ans++;
			}
		}
	}
	//cout << ans << endl;
	while (true) {
		int ad = 0; bool fin = false;
		rep(i, m) {

			rep(j, m) {
				if (i == j)continue;
				if (!b[i] && !b[j] && e[i][j]) {
					//cout << "hello" << endl;
					ad = 1;
					b[i] = b[j] = true;
					aite[i] = j;
					aite[j] = i;
					fin = true; break;
				}
			}
			if (fin)break;
		}
		if (fin) {
			ans += ad;
			continue;
		}
		rep(i, m) {
			if (!b[i]) {
				vector<int> flag(m, -1);
				queue<int> q;
				vector<bool> visited(m, false);
				visited[i] = true;
				flag[i] = i;
				q.push(i);
				while (!q.empty()) {
					int id = q.front(); q.pop();
					rep(j, m) {
						if (!e[id][j])continue;
						if (visited[j])continue;
						if (!b[j]) {
							vector<int> roots;
							roots.push_back(j);
							int cur = id;
							while (flag[cur]!=cur) {
								roots.push_back(cur);
								roots.push_back(aite[cur]);
								cur = flag[cur];

							}
							roots.push_back(cur);
							rep(k, roots.size() / 2) {
								int le = roots[2 * k];
								int ri = roots[2 * k + 1];
								aite[le] = ri;
								aite[ri] = le;
								b[le] = b[ri] = true;
							}
							fin = true; break;
						}
						else {
							int to = aite[j];
							if (visited[to])continue;
							visited[to] = true;
							flag[to] = id;
							q.push(to);
						}
					}
					if (fin)break;
				}
				if(fin)break;
			}
		}
		if (fin)ans++;
		else break;

	}

	cout << ans << endl;
}
signed main() {
	cin.tie(0);
	ios::sync_with_stdio(false);
	solve();
	//stop
		return 0;
}


Measured memory_bytes: 28072