#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXM = 2e6 + 5;
int N, M, V, st, se;
struct node {
  int x, y, z;
  int id;
} p[5005];
int hash_weapon[MAXN], cnt_p;
struct edge {
  int to, next, cf;
} e[MAXM];
int head[MAXN], cnt_adj;
void add_edge(int, int, int);
int Dinic(int, int);
int buck[MAXN];
struct ST {
  int left, right;
  int id;
  int move;
} sst[MAXN];
int nid[MAXN], hash_id[MAXN], newid, hash_sst[MAXN];
void build(int pos, int l, int r);
void update(int pos, int l, int r, int x);
int mark[MAXN];
struct node1 {
  int pos, u;
  int id;
};
vector<node1> A;
bool cmp1(node1 a, node1 b) {
  return sst[a.pos].right - sst[a.pos].left <
         sst[b.pos].right - sst[b.pos].left;
}
int main() {
  scanf("%d%d", &N, &M);
  st = N + 4 * M + 1, se = st + 1;
  cnt_adj = 1;
  V = se;
  newid = N;
  build(1, 1, M);
  for (int i = 1; i <= N; i++) {
    int op;
    scanf("%d", &op);
    mark[i] = op;
    if (op == 0) {
      add_edge(st, i, 1);
      add_edge(i, st, 0);
      int K;
      scanf("%d", &K);
      while (K--) {
        int x;
        scanf("%d", &x);
        add_edge(i, nid[x], 1);
        add_edge(nid[x], i, 0);
      }
    } else if (op == 1) {
      add_edge(st, i, 1);
      add_edge(i, st, 0);
      int x, y;
      scanf("%d%d", &x, &y);
      update(1, x, y, i);
    } else {
      add_edge(st, i, 2);
      add_edge(i, st, 0);
      cnt_p++;
      scanf("%d%d%d", &p[cnt_p].x, &p[cnt_p].y, &p[cnt_p].z);
      p[cnt_p].id = i;
      add_edge(i, nid[p[cnt_p].x], 1);
      add_edge(nid[p[cnt_p].x], i, 0);
      add_edge(i, nid[p[cnt_p].y], 1);
      add_edge(nid[p[cnt_p].y], i, 0);
      add_edge(i, nid[p[cnt_p].z], 1);
      add_edge(nid[p[cnt_p].z], i, 0);
    }
  }
  int ans = Dinic(st, se);
  for (int u = 1; u <= N; u++)
    if (mark[u] != 1)
      for (int i = head[u]; i; i = e[i].next) {
        if (!e[i].cf) {
          hash_weapon[hash_id[e[i].to]] = u;
          continue;
        }
      }
  for (int u = 1; u <= N; u++)
    if (mark[u] == 1) {
      for (int i = head[u]; i; i = e[i].next) {
        if (!e[i].cf) {
          A.push_back(node1{hash_sst[e[i].to], u});
          break;
        }
      }
    }
  sort(A.begin(), A.end(), cmp1);
  for (auto item : A) {
    while (hash_weapon[sst[item.pos].left + sst[item.pos].move] &&
           sst[item.pos].left + sst[item.pos].move <= sst[item.pos].right)
      sst[item.pos].move++;
    if (sst[item.pos].left + sst[item.pos].move <= sst[item.pos].right)
      hash_weapon[sst[item.pos].left + sst[item.pos].move] = item.u;
    sst[item.pos].move++;
  }
  for (int i = 1; i <= cnt_p; i++) {
    int x, y, z;
    int hx, hy, hz;
    x = p[i].x, y = p[i].y, z = p[i].z;
    hx = hash_weapon[x], hy = hash_weapon[y], hz = hash_weapon[z];
    buck[hx]++, buck[hy]++, buck[hz]++;
    if (buck[p[i].id] == 1) {
      if (hash_weapon[x] != p[i].id) {
        hash_weapon[x] = p[i].id;
      } else if (hash_weapon[y] != p[i].id) {
        hash_weapon[y] = p[i].id;
      } else if (hash_weapon[z] != p[i].id) {
        hash_weapon[z] = p[i].id;
      }
    }
    buck[hx]--, buck[hy]--, buck[hz]--;
  }
  printf("%d\n", ans);
  for (int i = 1; i <= M; i++)
    if (hash_weapon[i]) printf("%d %d\n", hash_weapon[i], i);
  return 0;
}
void build(int pos, int l, int r) {
  sst[pos].left = l, sst[pos].right = r, sst[pos].id = ++newid;
  hash_sst[newid] = pos;
  if (l == r) {
    nid[l] = newid;
    hash_id[newid] = l;
    add_edge(nid[l], se, 1);
    add_edge(se, nid[l], 0);
    return;
  }
  int lc = pos << 1, rc = lc + 1;
  int mid = (l + r) >> 1;
  build(lc, l, mid);
  build(rc, mid + 1, r);
  add_edge(sst[pos].id, sst[lc].id, 1e9);
  add_edge(sst[lc].id, sst[pos].id, 0);
  add_edge(sst[pos].id, sst[rc].id, 1e9);
  add_edge(sst[rc].id, sst[pos].id, 0);
}
void update(int pos, int l, int r, int x) {
  if (l <= sst[pos].left && sst[pos].right <= r) {
    add_edge(x, sst[pos].id, 1);
    add_edge(sst[pos].id, x, 0);
    return;
  }
  int lc = pos << 1, rc = lc + 1;
  int mid = (sst[pos].left + sst[pos].right) >> 1;
  if (l <= mid) update(lc, l, r, x);
  if (mid < r) update(rc, l, r, x);
}
int dep[MAXN], cur[MAXN];
bool Dinic_bfs(int, int);
int Dinic_dfs(int, int, int);
int Dinic(int st, int se) {
  int maxflow = 0;
  while (Dinic_bfs(st, se)) {
    for (int i = 1; i <= V; i++) cur[i] = head[i];
    int val;
    while ((val = Dinic_dfs(st, se, 1e9))) {
      maxflow += val;
    }
  }
  return maxflow;
}
bool Dinic_bfs(int st, int se) {
  queue<int> Q;
  memset(dep, 0, sizeof(dep));
  dep[st] = 1;
  Q.push(st);
  while (!Q.empty()) {
    int u = Q.front();
    Q.pop();
    for (int i = head[u]; i; i = e[i].next) {
      int v = e[i].to, cf = e[i].cf;
      if (cf && !dep[v]) {
        dep[v] = dep[u] + 1;
        Q.push(v);
      }
    }
  }
  return dep[se] != 0;
}
int Dinic_dfs(int u, int se, int cf_min) {
  if (u == se || !cf_min) return cf_min;
  for (int &i = cur[u]; i; i = e[i].next) {
    int v = e[i].to, cf = e[i].cf;
    if (cf && dep[v] == dep[u] + 1) {
      int val = Dinic_dfs(v, se, min(cf_min, cf));
      if (val) {
        e[i].cf -= val;
        e[i ^ 1].cf += val;
        return val;
      }
    }
  }
  return 0;
}
void add_edge(int x, int y, int z) {
  e[++cnt_adj].to = y;
  e[cnt_adj].cf = z;
  e[cnt_adj].next = head[x];
  head[x] = cnt_adj;
}
