#include <bits/stdc++.h>
using namespace std;
const int inf = 1e9;
int head[444444], to[444444], cap[444444], dist[444444], nxt[444444],
    cur[444444], tnode[444444], mx, ans, tp, cnp, L[444444], R[444444];
int n, m, s, t, cnt, x, y, z;
bool used[444444];
void addedge(int s, int t, int cps, int cpt) {
  cnt++;
  nxt[cnt] = head[s];
  head[s] = cnt;
  to[cnt] = t;
  cap[cnt] = cps;
  cnt++;
  nxt[cnt] = head[t];
  head[t] = cnt;
  to[cnt] = s;
  cap[cnt] = cpt;
}
void bfs() {
  memset(dist, -1, sizeof(dist));
  queue<int> q;
  q.push(s);
  dist[s] = 0;
  while (!q.empty()) {
    int x = q.front();
    q.pop();
    for (int i = head[x]; i; i = nxt[i]) {
      if (!cap[i]) continue;
      if (!~dist[to[i]]) {
        dist[to[i]] = dist[x] + 1;
        q.push(to[i]);
      }
    }
  }
}
int dfs(int i, int f) {
  if (i == t) return f;
  int tmp = f;
  for (int &j = cur[i]; j; j = nxt[j]) {
    if (!cap[j] || dist[to[j]] != dist[i] + 1) continue;
    int d = dfs(to[j], min(cap[j], f));
    if (d) {
      cap[j] -= d;
      cap[j ^ 1] += d;
      tmp -= d;
      if (!tmp) break;
    } else
      dist[to[j]] = 0;
  }
  return f - tmp;
}
int dinic() {
  int res = 0;
  while (1) {
    for (int i = 1; i <= t; i++) cur[i] = head[i];
    bfs();
    if (!~dist[t]) break;
    res += dfs(s, 1e9);
  }
  return res;
}
void build(int l, int r, int node) {
  mx = max(mx, node);
  L[node] = l;
  R[node] = r;
  if (l == r) {
    tnode[l] = node;
    return;
  }
  int mid = (l + r) >> 1;
  build(l, mid, node << 1);
  build(mid + 1, r, (node << 1) | 1);
  addedge(node, node << 1, inf, 0);
  addedge(node, (node << 1) | 1, inf, 0);
}
void update(int beg, int en, int l, int r, int node, int p) {
  if (l > en || r < beg) return;
  if (l >= beg && r <= en) {
    addedge(p, node, 1, 0);
    return;
  }
  int mid = (l + r) >> 1;
  update(beg, en, l, mid, node << 1, p);
  update(beg, en, mid + 1, r, (node << 1) | 1, p);
}
int Search(int l, int r, int node) {
  if (l == r) return l;
  int mid = (l + r) >> 1;
  for (int j = head[node]; j; j = nxt[j]) {
    if (cap[j ^ 1] &&
        ((to[j] == (node << 1)) || (to[j] == ((node << 1) | 1)))) {
      cap[j ^ 1]--;
      if (to[j] == (node << 1)) return Search(l, mid, node << 1);
      return Search(mid + 1, r, (node << 1) | 1);
    }
  }
}
int main() {
  cnt = 1;
  scanf("%d%d", &n, &m);
  build(1, m, 1);
  s = n + mx + 1;
  t = s + 1;
  for (int i = 1; i <= n; i++) {
    scanf("%d", &tp);
    if (!tp) {
      scanf("%d", &cnp);
      for (int j = 1; j <= cnp; j++) {
        scanf("%d", &x);
        addedge(mx + i, tnode[x], 1, 0);
      }
      addedge(s, mx + i, 1, 0);
    } else if (tp == 1) {
      scanf("%d%d", &x, &y);
      update(x, y, 1, m, 1, mx + i);
      addedge(s, mx + i, 1, 0);
    } else {
      scanf("%d%d%d", &x, &y, &z);
      used[x] = used[y] = 1;
      ans += 2;
      addedge(mx + i, tnode[x], 0, 1);
      addedge(mx + i, tnode[y], 0, 1);
      addedge(mx + i, tnode[z], 1, 0);
      addedge(s, mx + i, 0, 2);
    }
  }
  for (int i = 1; i <= m; i++) {
    if (!used[i])
      addedge(tnode[i], t, 1, 0);
    else
      addedge(tnode[i], t, 0, 1);
  }
  ans += dinic();
  printf("%d\n", ans);
  for (int i = 1; i <= n; i++) {
    for (int j = head[mx + i]; j; j = nxt[j]) {
      if (!cap[j] && to[j] != s) {
        printf("%d %d\n", i, Search(L[to[j]], R[to[j]], to[j]));
      }
    }
  }
  return 0;
}
