#include <bits/stdc++.h>
using namespace std;
class edge {
 public:
  int e, nxt, cap;
  edge(int e1 = -1, int nxt1 = -1, int cap1 = 0) {
    e = e1, nxt = nxt1, cap = cap1;
  }
};
int S, T, n, m, sum;
int dep[25010];
bool vis[25010];
class graph {
 public:
  edge g[5000000];
  int head[25010], tmpcnt;
  int cur[25010];
  void init() {
    memset(head, 255, sizeof(head));
    tmpcnt = 0;
  }
  int addedge(int s, int e, int cap) {
    g[tmpcnt] = edge(e, head[s], cap), head[s] = tmpcnt++;
    g[tmpcnt] = edge(s, head[e], 0), head[e] = tmpcnt++;
    return tmpcnt - 2;
  }
  bool bfs() {
    memset(vis, 0, sizeof(vis));
    memset(dep, 0, sizeof(dep));
    queue<int> q;
    q.push(S);
    vis[S] = 1;
    while (!q.empty()) {
      int fr = q.front();
      q.pop();
      for (int i = head[fr]; i != -1; i = g[i].nxt) {
        if (g[i].cap > 0 && !vis[g[i].e]) {
          dep[g[i].e] = dep[fr] + 1;
          vis[g[i].e] = 1;
          q.push(g[i].e);
        }
      }
    }
    return vis[T];
  }
  int dfs(int np, int maxflow) {
    int f;
    int flow = 0;
    if (np == T || !maxflow) return maxflow;
    for (int i = cur[np]; i != -1; i = g[i].nxt) {
      cur[np] = i;
      if (g[i].cap > 0 && dep[g[i].e] == dep[np] + 1 &&
          (f = dfs(g[i].e, min(g[i].cap, maxflow))) > 0) {
        g[i].cap -= f;
        g[i ^ 1].cap += f;
        flow += f;
        maxflow -= f;
        if (!maxflow) break;
      }
    }
    return flow;
  }
  int dinic() {
    int out = 0;
    while (bfs()) memcpy(cur, head, sizeof(cur)), out += dfs(S, 0x3f3f3f3f);
    return out;
  }
} G;
int rt, ch[25010][2], nodecnt, id[5010], iid[25010], efa[25010];
void build(int &p, int l, int r) {
  p = ++nodecnt;
  if (l == r) {
    id[l] = p;
    iid[p] = l;
    return;
  }
  int mid = (l + r) >> 1;
  build(ch[p][0], l, mid);
  build(ch[p][1], mid + 1, r);
  efa[ch[p][0]] = G.addedge(p, ch[p][0], 0x3f3f3f3f);
  efa[ch[p][1]] = G.addedge(p, ch[p][1], 0x3f3f3f3f);
}
void link(int p, int l, int r, int ml, int mr, int st, int w) {
  if (ml <= l && r <= mr) {
    G.addedge(st, p, w);
    return;
  }
  int mid = (l + r) >> 1;
  if (ml <= mid) link(ch[p][0], l, mid, ml, mr, st, w);
  if (mr > mid) link(ch[p][1], mid + 1, r, ml, mr, st, w);
}
int ANS[5010], cnt[5010], etoT[5010];
vector<int> operate(int p, int l, int r) {
  vector<int> ret;
  if (l == r) {
    if (G.g[etoT[l] ^ 1].cap) ret.push_back(l);
  } else {
    int mid = (l + r) >> 1;
    ret = operate(ch[p][0], l, mid);
    vector<int> tmp = operate(ch[p][1], mid + 1, r);
    ret.insert(ret.end(), tmp.begin(), tmp.end());
  }
  for (int i = G.head[p]; i != -1; i = G.g[i].nxt) {
    if (G.g[i].e <= n && G.g[i].cap) {
      ANS[ret.back()] = G.g[i].e;
      ret.pop_back();
    }
  }
  return ret;
}
int opt[5010], val[5010][3];
int main() {
  G.init();
  scanf("%d%d", &n, &m);
  nodecnt = n;
  build(rt, 1, m);
  S = nodecnt + 1;
  T = nodecnt + 2;
  for (int i = 1; i <= m; i++) etoT[i] = G.addedge(id[i], T, 1);
  for (int i = 1, ti, tj; i <= n; i++) {
    scanf("%d", &opt[i]);
    if (opt[i] == 0) {
      scanf("%d", &ti);
      for (int j = 1; j <= ti; j++) {
        scanf("%d", &tj);
        G.addedge(i, id[tj], 1);
      }
      G.addedge(S, i, 1);
    } else if (opt[i] == 1) {
      scanf("%d%d", &ti, &tj);
      link(rt, 1, m, ti, tj, i, 1);
      G.addedge(S, i, 1);
    } else {
      for (int j = 0; j < 3; j++)
        scanf("%d", &val[i][j]), G.addedge(i, id[val[i][j]], 1);
      G.addedge(S, i, 2);
    }
  }
  G.dinic();
  int ans = 0;
  operate(rt, 1, m);
  for (int i = 1; i <= m; i++) cnt[ANS[i]]++, ans += (ANS[i] != 0);
  for (int i = 1; i <= n; i++) {
    if (opt[i] == 2) {
      if (cnt[i] == 1) {
        for (int j = 0; j < 3; j++) {
          if (ANS[val[i][j]] != i) {
            ANS[val[i][j]] = i;
            break;
          }
        }
      }
    }
  }
  printf("%d\n", ans);
  for (int i = 1; i <= m; i++)
    if (ANS[i]) printf("%d %d\n", ANS[i], i);
  return 0;
}
