#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <set>
#include <unordered_map>
#include <cassert>
#include <cstdint>
#include <array>




class Xoshiro256pp{
public:

    using i64 = long long;
    using u64 = unsigned long long;


private:
    std::array<u64, 4> s;

    static inline uint64_t rotl(const uint64_t x, int k) noexcept {
        return (x << k) | (x >> (64 - k));
    }
    inline uint64_t gen(void) noexcept {
        const uint64_t result = rotl(s[0] + s[3], 23) + s[0];
        const uint64_t t = s[1] << 17;
        s[2] ^= s[0];
        s[3] ^= s[1];
        s[1] ^= s[2];
        s[0] ^= s[3];
        s[2] ^= t;
        s[3] = rotl(s[3], 45);
        return result;
    }

public:

    Xoshiro256pp(){
        s[0] = 0x8e1281ee837ab726;
        s[1] = 0xbe54f8d4f619af0d;
        s[2] = 0x0d989b2579a56e23;
        s[3] = 0x7b3c16005b6dddf9;
    }
    
    u64 rng64() { return gen(); }

    // generate x : l <= x <= r
    u64 random_unsigned(u64 l,u64 r){
        assert(l<=r);
        r-=l;
        auto res = rng64();
        if(res<=r) return res+l;
        u64 d = r+1;
        u64 max_valid = 0xffffffffffffffff/d*d;
        while(true){
            auto res = rng64();
            if(res<=max_valid) break;
        }
        return res%d+l;
    }


};




using namespace std;

#define rep(i,n) for(int i=0; i<int(n); i++)

pair<int, vector<int>> bin_packing(vector<int> weight){
    int N = weight.size();
    int N_original = N;

    int odd_count = 0;
    for(int a : weight) if(a % 2 == 1) odd_count += 1;
    int sum = N * 2 - 2;
    int Q = *max_element(weight.begin(), weight.end());;
    while(true){
        int req = sum;
        if(Q % 2 == 1) req += max(0, Q - odd_count);
        if(req <= (long long)Q * Q) break;
        Q += 1;
    }

    int P = (sum + Q - 1) / Q;
    if(P < Q) P += 1;
    while(sum < P*Q){
        weight.push_back(1);
        sum += 1;
        N += 1;
        odd_count += 1;
    }

    set<pair<int,int>> even_group, odd_group;
    rep(i,N) (weight[i] % 2 ? odd_group : even_group).insert({ -weight[i], i });
    even_group.insert({ 1, -100 });
    odd_group.insert({ 1, -100 });

    vector<int> ans(N, -1);
    auto set_ans = [&](int i, int c){
        if(i < 0) exit(1);
        (weight[i] % 2 ? odd_group : even_group).erase({ -weight[i], i });
        ans[i] = c;
        if(weight[i] % 2 == 1) odd_count -= 1;
    };
    auto extract_max_at = [&](set<pair<int,int>>& src, int x){
        pair<int,int> res = *src.lower_bound({ -x, -1 });
        res.first *= -1;
        return res;
    };
    auto extract_max = [&](int x){
        return max(extract_max_at(even_group, x), extract_max_at(odd_group, x));
    };
    
    rep(i,P){
        int to_pack = Q;
        if(Q % 2 == 1){
            if(odd_count == P - i){
                auto [w,v] = extract_max_at(odd_group, to_pack);
                set_ans(v, i);
                to_pack -= w;
            }
            while(to_pack > 0){
                auto [w,v] = (odd_count == P-i-1) ? extract_max_at(even_group, to_pack) : extract_max(to_pack);
                set_ans(v, i);
                to_pack -= w;
            }
        } else {
            while(to_pack > 0){
                auto [w,v] = extract_max(to_pack);
                set_ans(v, i);
                to_pack -= w;
            }
        }
    }

    ans.resize(N_original);
    return {Q,ans};
}


#include <random>
#include <chrono>

void testcase(){
    // input

    int N; cin >> N;

    vector<vector<int>> edges(N);
    rep(i,N){
        int c; cin >> c;
        rep(j,c){
            int e; cin >> e; e--;
            edges[i].push_back(e);
        }
    }

    // node color

    vector<int> deg(N);
    rep(i,N) deg[i] = edges[i].size();
    auto [Q, vcolor] = bin_packing(deg);

    // heuristic

    vector<int> bedge(N*2-2);
    vector<int> eVisCount(N-1, 0);
    rep(i,N) rep(j,edges[i].size()){
        int e = edges[i][j];
        bedge[e*2 + eVisCount[e]] = vcolor[i];
        edges[i][j] = e*2 + eVisCount[e];
        eVisCount[e]++;
    }
    
    Xoshiro256pp rng;

    vector<int> vacant(Q, 0);
    vector<int> bcolor(N*2-2);
    rep(e,N*2-2) bcolor[e] = vacant[bedge[e]]++;
    vector<vector<int>> maps(Q);
    rep(q,Q) maps[q].resize(vacant[q]+1);
    rep(e,N*2-2) maps[bedge[e]][bcolor[e]] = e;
    vector<int> conflict;
    rep(i,N-1) if(bcolor[i*2] == bcolor[i*2+1]) conflict.push_back(i);
    while(conflict.size()){
        int ex = conflict.back(); conflict.pop_back();
        int e0 = ex * 2 + 0;
        int e1 = ex * 2 + 1;
        if(bcolor[e0] != bcolor[e1]) continue;
        if(vacant[bedge[e0]] != Q){
            swap(vacant[bedge[e0]], bcolor[e0]);
            continue;
        }
        if(vacant[bedge[e1]] != Q){
            swap(vacant[bedge[e1]], bcolor[e1]);
            continue;
        }
        int f = (int)rng.random_unsigned(0, 2*(Q-1) - 1);
        if(f >= Q-1){ swap(e0, e1); f -= Q-1; }
        if(f >= bcolor[e0]) f++;
        int vcol = bedge[e0];
        int e0x = maps[vcol][f];
        swap(bcolor[e0x], bcolor[e0]);
        swap(maps[vcol][f], maps[vcol][bcolor[e0]]);
        if(bcolor[e0x] == bcolor[e0x^1]){
            conflict.push_back(e0x / 2);
        }
    }

    // output

    cout << Q << '\n';
    rep(i,N){
        cout << (vcolor[i] + 1);
        for(auto e : edges[i]) cout << ' ' << (bcolor[e] + 1);
        cout << '\n';
    }
}

int main(){
    ios::sync_with_stdio(false); cin.tie(nullptr);
    int T; cin >> T;
    rep(ti,T) testcase();
    return 0;
}