#include <bits/stdc++.h>
using namespace std;
struct Edge {
  int u, v, cap, flow;
  Edge() {}
  Edge(int u, int v, int flow, int cap) : u(u), v(v), flow(flow), cap(cap) {}
};
struct Dinic {
  vector<int> d, ptr, reach;
  vector<Edge> e;
  vector<vector<int> > g;
  int n, s, t;
  const int inf = 1e9;
  Dinic() {}
  Dinic(int n, int s, int t) : n(n), t(t), s(s), d(n), ptr(n), g(n) {}
  void addEdge(int u, int v, int cap) {
    g[u].push_back(e.size());
    e.push_back(Edge(u, v, 0, cap));
    g[v].push_back(e.size());
    e.push_back(Edge(v, u, 0, 0));
  }
  int MaxFlow() {
    int flow = 0;
    while (bfs()) {
      while (int x = dfs(s, inf)) {
        flow += x;
      }
    }
    return flow;
  }
  vector<pair<int, int> > matching;
  void FindMatching(int z) {
    reach.resize(e.size());
    dfs2(s, -1, z);
  }

 private:
  bool bfs() {
    for (int i = 0; i < (n); ++i) ptr[i] = 0, d[i] = -1;
    d[s] = 0;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
      int u = q.front();
      q.pop();
      for (int id : g[u])
        if (e[id].cap > e[id].flow) {
          int v = e[id].v;
          if (d[v] == -1) {
            d[v] = d[u] + 1;
            q.push(v);
          }
        }
    }
    return d[t] != -1;
  }
  int dfs(int u, int flow) {
    if (u == t) return flow;
    for (; ptr[u] < (int)g[u].size(); ++ptr[u]) {
      int id = g[u][ptr[u]];
      int v = e[id].v;
      if (d[v] == d[u] + 1 && e[id].flow < e[id].cap) {
        if (int x = dfs(v, min(flow, e[id].cap - e[id].flow))) {
          e[id].flow += x;
          e[id ^ 1].flow -= x;
          return x;
        }
      }
    }
    return 0;
  }
  bool dfs2(int u, int p, int lim) {
    for (int id : g[u])
      if (e[id].cap > 0 && e[id].flow > 0) {
        int v = e[id].v;
        if (v <= lim && v != t) {
          if (p == -1) {
            if (dfs2(v, v, lim)) e[id].flow--;
          } else if (dfs2(v, p, lim)) {
            e[id].flow--;
            return 1;
          }
        } else {
          matching.push_back(pair<int, int>(p, u));
          e[id].flow--;
          return 1;
        }
      }
    return 0;
  }
};
int OMG[1010100], id[1010100 * 4], pos[1010100];
vector<int> SQL[1010100];
void Build(int k, int l, int r, Dinic &D, int t) {
  if (l == r) {
    id[k] = l;
    for (int u : SQL[l]) D.addEdge(u, t + k, 1);
    if (!OMG[l])
      D.addEdge(t + k, t, 1);
    else
      D.addEdge(t + k, OMG[l], 1);
    return;
  }
  int m = (l + r) >> 1;
  D.addEdge(t + k, t + k * 2, 1e9);
  D.addEdge(t + k, t + k * 2 + 1, 1e9);
  Build(k << 1, l, m, D, t);
  Build(k << 1 | 1, m + 1, r, D, t);
}
void Up(int k, int l, int r, int u, int v, Dinic &D, int s, int t) {
  if (l > v || r < u) return;
  if (u <= l && r <= v) {
    D.addEdge(s, k + t, 1);
    return;
  }
  int m = (l + r) >> 1;
  Up(k << 1, l, m, u, v, D, s, t);
  Up(k << 1 | 1, m + 1, r, u, v, D, s, t);
}
vector<int> e[1010100];
int type[1010100], mark[1010100];
int main() {
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
  int n, m;
  cin >> n >> m;
  int s = 0, t = n + 1;
  Dinic D(2 * n + m * 4 + 2, s, t);
  int cnt = n + m * 4 + 1;
  for (int i = (1); i <= (n); ++i) {
    cin >> type[i];
    if (type[i] == 0) {
      D.addEdge(s, i, 1);
      int k;
      cin >> k;
      while (k--) {
        int x;
        cin >> x;
        SQL[x].push_back(i);
      }
    }
    if (type[i] == 1) {
      D.addEdge(s, i, 1);
      int l, r;
      cin >> l >> r;
      Up(1, 1, m, l, r, D, i, t);
    }
    if (type[i] == 2) {
      ++cnt;
      D.addEdge(cnt, t, 1);
      e[i].resize(3);
      for (int j = 0; j < (3); ++j) {
        cin >> e[i][j];
        OMG[e[i][j]] = cnt;
      }
    }
  }
  Build(1, 1, m, D, t);
  D.MaxFlow();
  D.FindMatching(n + m * 4 + 1);
  vector<pair<int, int> > ans;
  for (pair<int, int> ee : D.matching) {
    int x = ee.first;
    int y = id[ee.second - t];
    ans.push_back(pair<int, int>(x, y));
    mark[y] = 1;
  }
  for (int i = (1); i <= (n); ++i)
    if (type[i] == 2) {
      int cnt = 2;
      for (int v : e[i])
        if (!mark[v] && cnt) {
          cnt--;
          ans.push_back(pair<int, int>(i, v));
        }
    }
  cout << ans.size() << '\n';
  for (pair<int, int> x : ans) {
    cout << x.first << ' ' << x.second << '\n';
  }
}
