#include <bits/stdc++.h>
using namespace std;
void OUT_TST(string s, istream_iterator<string> it) { cerr << s; }
template <typename T, typename... Args>
void OUT_TST(string s, istream_iterator<string> it, T a, Args... args) {
  cerr << *it << "=" << a << " ";
  OUT_TST(s, ++it, args...);
}
template <class T>
void rd(T& ret) {
  ret = 0;
  bool ok = 0, u = 0;
  for (;;) {
    int c = getchar();
    if (c >= '0' && c <= '9')
      ret = (ret << 3) + (ret << 1) + c - '0', ok = 1;
    else if (c == '-')
      u = 1;
    else if (ok) {
      if (u) ret *= -1;
      return;
    }
  }
}
long long powmod(long long p, long long n, long long mod) {
  long long ret = 1;
  for (; n; n >>= 1) {
    if (n & 1) ret = ret * p % mod;
    p = p * p % mod;
  }
  return ret;
}
template <class T>
bool chmin(T& a, const T& b) {
  return b < a ? a = b, 1 : 0;
}
template <class T>
bool chmax(T& a, const T& b) {
  return b > a ? a = b, 1 : 0;
}
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
int main() {
  ios_base::sync_with_stdio(false), cin.tie(nullptr);
  int cas;
  cas = 1;
  for (int i = 0; i < cas; ++i) {
    auto solve = []() {
      int n, m;
      cin >> n >> m;
      vector<set<int>> an(n);
      vector<int> cnt(n), target_num(n, 0), idx(n);
      int total_cnt = 0;
      for (int i = 0; i < n; ++i) {
        an[i].clear();
        cin >> cnt[i];
        for (int j = 0; j < cnt[i]; ++j) {
          int first;
          cin >> first;
          an[i].insert(first);
        }
        total_cnt += cnt[i];
        idx[i] = i;
      }
      for (int i = 0, j = 0; i < total_cnt; ++i, j = (j + 1) % n) {
        target_num[j]++;
      }
      sort(idx.begin(), idx.end(), [&](int first, int second) {
        return an[first].size() > an[second].size();
      });
      if (n == 1) {
        cout << "0" << endl;
        return;
      }
      auto get_intersection = [](const set<int>& a, const set<int>& b,
                                 set<int>& res) {
        res.clear();
        for (int i : a) {
          if (!b.count(i)) {
            res.insert(i);
          }
        }
      };
      set<int> now;
      get_intersection(an[idx[0]], an[idx[n - 1]], now);
      using tp3 = tuple<int, int, int>;
      vector<tp3> ans;
      for (int i = 0, j = n - 1; i < j;) {
        if (target_num[i] == (int)an[idx[i]].size()) {
          if (++i == j) break;
          get_intersection(an[idx[i]], an[idx[j]], now);
        } else if (target_num[j] == (int)an[idx[j]].size()) {
          if (i == j - 1) break;
          for (int first : an[idx[j]]) {
            if (an[idx[i]].count(first)) {
              now.insert(first);
            }
          }
          --j;
          for (int first : an[idx[j]]) {
            auto it = now.find(first);
            if (it != now.end()) {
              now.erase(it);
            }
          }
        } else {
          int first = *now.begin();
          an[idx[i]].erase(first);
          an[idx[j]].insert(first);
          now.erase(now.begin());
          ans.emplace_back(idx[i], idx[j], first);
        }
      }
      cout << ans.size() << endl;
      for (auto& i : ans) {
        cout << get<0>(i) + 1 << " " << get<1>(i) + 1 << " " << get<2>(i)
             << endl;
      }
    };
    solve();
  }
  return 0;
}
