#include<iostream>
#include<vector>

#include <algorithm>
#include <cassert>
#include <cctype>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstring>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <vector>
#define rep(i, n) for (int i = 0; i < (int)(n); ++i)
//#define cerr if(false) cerr
#ifdef DEBUG
#define show(...) cerr << #__VA_ARGS__ << " = ", debug(__VA_ARGS__);
#else
#define show(...) 42
#endif
using namespace std;
using ll = long long;
using pii = pair<int, int>;
template <typename T, typename S>
ostream& operator<<(ostream& os, pair<T, S> a) {
    os << '(' << a.first << ',' << a.second << ')';
    return os;
}
template <typename T>
ostream& operator<<(ostream& os, vector<T> v) {
    for (auto x : v) os << x << ' ';
    return os;
}
void debug() {
    cerr << '\n';
}
template <typename H, typename... T>
void debug(H a, T... b) {
    cerr << a;
    if (sizeof...(b)) cerr << ", ";
    debug(b...);
}
const int N = 300;
struct MT{
    bool g[N][N];
    int mt[N];
};

bool maxmt(MT &mt){
    int ty[N] = {};
    int rt[N];
    int gr[N];
    int nw[N];
    fill_n(nw, N, -1);
    fill_n(rt, N, -1);
    fill_n(gr, N, -1);
    using P = pair<int, int>;
    queue<int> q;
    for(int i = 0; i < N; i++){
        if(mt.mt[i] == -1){
            rt[i] = gr[i] = i;
            ty[i] = -1;
            q.push(i);
        }
    }
    auto get_path = [&](int u){
        vector<int> v;
        while(rt[u] != u){
            v.push_back(u);
            v.push_back(mt.mt[u]);
            u = nw[mt.mt[u]];
        }
        v.push_back(u);
        return v;
    };
    while(q.size()){
        int a = q.front(); q.pop();
        for(int b = 0; b < N; b++){
            if(!mt.g[a][b] or ty[b] == 1)continue;
            if(ty[b] == -1 and gr[a] == gr[b])continue;
            if(ty[b] == 0){
                int c = mt.mt[b];
                nw[b] = a; nw[c] = -1;
                rt[b] = rt[c] = rt[a];
                gr[b] = b; gr[c] = c;
                ty[b] = 1; ty[c] = -1;
                q.push(c);
                continue;
            }
            if(rt[a] != rt[b]){
                for(int c : {a, b}){
                    auto v = get_path(c);
                    int m = (int ) v.size();
                    assert(m % 2 == 1);
                    for(int i = 1; i < m; i+=2){
                        mt.mt[v[i]] = v[i+1];
                        mt.mt[v[i+1]] = v[i];
                    }
                }
                mt.mt[a] = b;
                mt.mt[b] = a;
                return true;
            }
            auto v1 = get_path(a);
            auto v2 = get_path(b);
            reverse(v1.begin(), v1.end());
            reverse(v2.begin(), v2.end());
            assert(v1[0] == v2[0]);
            int m = min(v1.size(),  v2.size()), lca;
            for(int i = 1; i <= m; i++){
                if(i == m or v1[i] != v2[i]){
                    lca = gr[v1[i-1]];
                    break;
                }
            }
            bool used[N] = {};
            fill_n(used,N, false);
            for(int c: {a,b}){
                auto v = get_path(c);
                int m = (int) v.size();
                for(int i = 0; gr[v[i]] != lca; i+= 2){
                    used[gr[v[i]]] = used[gr[v[i+1]]] = true;
                    if(ty[v[i+1]] == 1){
                        ty[v[i+1]] = -1;
                        q.push(v[i+1]);
                    }
                    if(i) nw[v[i]] = v[i-1];
                }
            }
            if(gr[a] != lca) nw[a] = b;
            if(gr[b] != lca) nw[b] = a;
            for(int i = 0; i < N; i++){
                if(ty[i] and used[gr[i]]){
                    gr[i] = lca;
                }
            }
        }
    }
    return false;
}
MT first;
int solve(){
    int co = 0;
    while(maxmt(first)){
        co++;
    }
    return co;
}
void add_edge(int a, int b){
    first.g[a][b] = first.g[b][a] = true;
}


using namespace std;
#define int long long

vector<int> V;

signed main(){
    memset(first.g, 0, sizeof(first.g));
    memset(first.mt, -1, sizeof(first.mt));
    int X, M;
    cin >>X>>M;
    V.resize(X, 0);
    vector<vector<int> > A(M);
    vector<int> C(X, 0);
    vector<vector<int> > B(X);
    for(int i=0;i<M;i++){
        int k;
        cin >>k;
        A[i].resize(k);
        for(int j=0;j<k;j++){
            cin >> A[i][j];A[i][j]--;
            C[A[i][j]]++;
            B[A[i][j]].push_back(i);
        }
    }
    int res =0;
    for(int i=0;i<X;i++){
        if(C[i] == 0){
            res++;
            V[i] = 1;
        }
        if(C[i] == 1 && V[i] == 0){
            res++;
            for(int j=0;j<B[i].size();j++){
                for(int k=0;k < A[B[i][j]].size();k++){
                    V[A[B[i][j]][k]]=1;
                }
            }
        }
    }
    B.clear();
    B.resize(X);
    for(int i=0;i<M;i++){
        for(int j=(int)A[i].size() -1;j >= 0;j--){
            if(V[A[i][j]] == 1){
                swap(A[i][j], A[i].back());
                A[i].pop_back();
            }
        }
    }
    

    for(int i=0;i<M;i++){
        for(int j=0;j < A[i].size();j++){
           B[A[i][j]].push_back(i);
        }
    }
    vector<vector<int> > D(M,vector<int>(M, 0));
    for(int i=0;i<X;i++){
     
        if(B[i].size() == 2){
            if(D[B[i][0]][B[i][1]] == 0){
                add_edge(B[i][0], B[i][1]);
                D[B[i][0]][B[i][1]] = 1;
                D[B[i][1]][B[i][0]] = 1;
            }
        }
    }
    //cerr << "HI"<<endl;
    res += solve();

    cout << res << endl;
   
}


Measured memory_bytes: 12284