#include <bits/stdc++.h>
using namespace std; 

#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define FOR(i, a, b) for( int i = a; i < b; i++ )
#define bug(x) cout << #x << " " << x << endl;

using ll = long long; 
using vi = vector<int>; 
using pii = pair<int, int>; 

/*
    Para cada pessoa, sejam: 

        l[i] = primeiro amigo a esquerda de i
        r[i] = primeiro amigo a direita de i

    Qualquer intervalo pertencente a [l[i] + 1, i] x [i, r[i] - 1] eh invalido. 

    Pensando nos intervalos (l, r) como pontos no plano ( em que l eh a coordenada x e r eh a coordenada y ),
    cada amigo define um retangulo invalido. 

    Assim, basta calcular quantos pontos nao estao dentro de um retangulo. 
    Vale notar que todo ponto em que l > r tambem eh invalido
*/

struct Segtree{
    struct Node{
        int minimo, quantidade, lazy; 
        Node( int minimo = 0, int quantidade = 0 ) : minimo(minimo), quantidade(quantidade), lazy(0) {}
        Node operator + ( const Node &o ) const {
            if( minimo < o.minimo ) return *this; 
            if( o.minimo < minimo ) return o; 
            return Node( minimo, quantidade + o.quantidade ); 
        }
    }; 

    vector<Node> seg; 
    int n; 

    void build( int node, int ti, int tf ){
        if( ti == tf ){ seg[node] = Node(0, 1); return; }
        int l = 2*node, r = 2*node + 1, tm = (ti + tf)/2; 
        build( l, ti, tm ); build( r, tm + 1, tf ); 
        seg[node] = seg[l] + seg[r]; 
    }

    Segtree( int n ) : n(n), seg(4*n) {
        build(1, 0, n - 1); 
    }

    void refresh( int node, int ti, int tf ){
        if( seg[node].lazy == 0 ) return; 
        int x = seg[node].lazy; seg[node].lazy = 0; 

        seg[node].minimo += x; 

        if( ti == tf ) return; 

        seg[2*node].lazy += x; 
        seg[2*node + 1].lazy += x; 
    }

    void update( int node, int ti, int tf, int qi, int qf, int x ){
        refresh(node, ti, tf); 
        if( qi > tf || ti > qf ) return;
        if( qi <= ti && tf <= qf ){ seg[node].lazy = x; refresh(node, ti, tf); return; }
        int l = 2*node, r = 2*node + 1, tm = (ti + tf)/2; 
        update( l, ti, tm, qi, qf, x ); update( r, tm + 1, tf, qi, qf, x ); 
        seg[node] = seg[l] + seg[r];
    }

    // Somar x no intervalo [l, r]
    void update( int l, int r, int x ){
        update( 1, 0, n - 1, l, r, x ); 
    }

    // Retorna a quantidade de 0s na seg
    int query(){
        return seg[1].minimo == 0 ? seg[1].quantidade : 0; 
    }
};

struct Event{
    int x; // Coordenada x 
    int y1, y2; // Coordenadas y
    int val; // +1 -> inicio do retangulo, -1 -> final do retangulo 

    bool operator < ( const Event &o ) const {
        return ((x == o.x) ? val < o.val : x < o.x);
    }
}; 

ll solve( int n, vector<Event> &v ){
    sort(all(v)); 
    Segtree seg(n); 

    ll ans = 0; 
    int previous_x = 0; 
    for( auto [x, y1, y2, val] : v ){
        ans += 1LL*(x - previous_x)*seg.query(); 

        seg.update(y1, y2, val);
        previous_x = x; 
    }
    return ans; 
}

void solve(){
    int n; cin >> n; 

    vector<Event> v; 

    auto create_rectangle = [&]( int x1, int x2, int y1, int y2 ){
        v.push_back({ x1, y1, y2, 1 }); 
        v.push_back({ x2 + 1, y1, y2, -1 });
    }; 

    FOR(i, 0, n){
        // Intervalos com l > i e r = i sao invalidos
        if( i + 1 < n ) create_rectangle( i + 1, n - 1, i, i );

        int k; cin >> k; 
        if( k == 0 ) continue; 

        int l = -1, r = n; 
        while( k-- ){
            int j; cin >> j; j--; 
            if( j < i ) l = max( l, j ); 
            else r = min( r, j ); 
        }

        create_rectangle( l + 1, i, i, r - 1 ); 
    }

    cout << solve(n, v) << '\n';
}

int main(){
    ios::sync_with_stdio(false); cin.tie(NULL); 
    solve(); 
}