#include <bits/stdc++.h>
using namespace std;
const int INFL = (int)1e9;
const long long int INFLL = (long long int)1e18;
const double INFD = numeric_limits<double>::infinity();
const double PI = 3.14159265358979323846;
long long int rndf(double x) {
  return (long long int)(x + (x >= 0 ? 0.5 : -0.5));
}
long long int floorsqrt(double x) {
  long long int m = (long long int)sqrt(x);
  return m + (m * m <= (long long int)(x) ? 0 : -1);
}
long long int ceilsqrt(double x) {
  long long int m = (long long int)sqrt(x);
  return m + ((long long int)x <= m * m ? 0 : 1);
}
long long int rnddiv(long long int a, long long int b) {
  return (a / b + (a % b * 2 >= b ? 1 : 0));
}
long long int ceildiv(long long int a, long long int b) {
  return (a / b + (a % b == 0 ? 0 : 1));
}
long long int gcd(long long int m, long long int n) {
  if (n == 0)
    return m;
  else
    return gcd(n, m % n);
}
class SegTree {
 private:
  vector<int> segs;
  void local_update(int index, int (*update_rule)(int, int)) {
    int l = left_of(index), r = index * 2 + 2;
    segs[index] = update_rule(segs[l], segs[r]);
  }
  void get_index_of_range_rec(int s, int t, int l, int r, int index,
                              vector<int>* v) {
    if (s == l && t == r)
      v->push_back(index);
    else {
      int mid = (l + r) / 2;
      if (s < mid && mid < t) {
        get_index_of_range_rec(s, mid, l, mid, left_of(index), v);
        get_index_of_range_rec(mid, t, mid, r, right_of(index), v);
      } else if (s < mid) {
        get_index_of_range_rec(s, t, l, mid, left_of(index), v);
      } else if (mid < t) {
        get_index_of_range_rec(s, t, mid, r, right_of(index), v);
      }
    }
  }

 public:
  int n;
  int left_of(int index) { return index * 2 + 1; }
  int right_of(int index) { return index * 2 + 2; }
  SegTree(vector<int> a, int init, int (*update_rule)(int, int)) {
    n = (int)pow(2, ceil(log2(a.size())));
    segs = vector<int>(n * 2 - 1, init);
    for (int i = 0; i < (int)a.size(); i++) segs[n - 1 + i] = a[i];
    for (int i = (int)n - 1 - 1; i >= 0; i--) local_update(i, update_rule);
  }
  void update(int k, int x, int (*update_rule)(int, int),
              bool add_flag = false) {
    int index = k + n - 1;
    segs[index] = add_flag ? segs[index] + x : x;
    while (index > 0) {
      index = (index - 1) / 2;
      local_update(index, update_rule);
    }
  }
  vector<int> get_index_of_range(int s, int t) {
    vector<int> ret = {};
    get_index_of_range_rec(s, t, 0, n, 0, &ret);
    return ret;
  }
  int sum_of_range(int s, int t) {
    vector<int> v = get_index_of_range(s, t);
    int ret = 0;
    for (int i = 0; i < (int)v.size(); i++) {
      ret += segs[v[i]];
    }
    return ret;
  }
};
int update_rule(int a, int b) { return a + b; }
struct graph_t {
  int n;
  int m;
  vector<pair<int, int>> edges;
  vector<long long int> vals;
  vector<long long int> costs;
  vector<long long int> caps;
};
class Maxflow {
 private:
  struct edgedata {
    int eid, from, to;
    long long int cap;
    int dual_p;
  };
  struct node {
    int id;
    bool done;
    vector<edgedata> edges;
    long long int d;
    bool operator<(const node& another) const {
      return !(d != another.d ? d < another.d : id < another.id);
    }
  };
  long long int dfs(int a, long long int c) {
    if (a == sink) return c;
    nodes[a].done = true;
    for (int i = 0; i < (int)nodes[a].edges.size(); i++) {
      int b = nodes[a].edges[i].to;
      if (!nodes[b].done && nodes[a].edges[i].cap > 0) {
        long long int df = dfs(b, min(c, nodes[a].edges[i].cap));
        if (df > 0) {
          nodes[a].edges[i].cap -= df;
          nodes[b].edges[nodes[a].edges[i].dual_p].cap += df;
          return df;
        }
      }
    }
    return 0;
  }
  vector<node> nodes;
  int n, m;
  int source, sink;
  edgedata* empty_edge;

 public:
  Maxflow(graph_t G, int s, int t) {
    n = G.n;
    m = G.m;
    nodes.resize(n);
    for (int i = 0; i < (int)n; i++) nodes[i] = {i, false, {}, LLONG_MAX};
    empty_edge = new edgedata;
    for (int i = 0; i < (int)G.edges.size(); i++) {
      int a = G.edges[i].first;
      int b = G.edges[i].second;
      nodes[a].edges.push_back(
          {i, a, b, G.caps[i], (int)nodes[b].edges.size()});
      nodes[b].edges.push_back(
          {i - m, b, a, 0, (int)nodes[a].edges.size() - 1});
    }
    source = s;
    sink = t;
    vector<pair<int, edgedata*>> stk;
    while (dfs(source, LLONG_MAX)) {
      for (int i = 0; i < (int)n; i++) nodes[i].done = false;
    }
    return;
  }
  vector<long long int> get_eid_flow() {
    vector<long long int> ret(m);
    for (int i = 0; i < (int)n; i++) {
      for (auto itr = nodes[i].edges.begin(); itr != nodes[i].edges.end();
           itr++) {
        if (itr->eid < 0) ret[itr->eid + m] = itr->cap;
      }
    }
    return ret;
  }
  long long int get_maxflow() {
    long long int ret = 0;
    for (auto itr = nodes[sink].edges.begin(); itr != nodes[sink].edges.end();
         itr++) {
      if (itr->eid < 0) ret += itr->cap;
    }
    return ret;
  }
  void solve(vector<bool> is_omg, int enemycnt) {
    vector<int> a(enemycnt, -1);
    for (int i = 0; i < (int)is_omg.size(); i++) {
      if (!is_omg[i]) {
        int focus = i + 1;
        while (true) {
          bool exist = false;
          if (focus == i + 1) {
            for (auto itr = nodes[focus].edges.begin();
                 itr != nodes[focus].edges.end(); itr++) {
              if (itr->to < n - 1 && itr->to > focus && itr->cap == 0) {
                focus = itr->to;
                exist = true;
                break;
              }
            }
          } else {
            for (auto itr = nodes[focus].edges.begin();
                 itr != nodes[focus].edges.end(); itr++) {
              if (itr->to < n - 1 && itr->to > focus && itr->cap < enemycnt) {
                focus = itr->to;
                itr->cap++;
                exist = true;
                break;
              }
            }
          }
          if (!exist) break;
        }
        if (n - 1 - enemycnt <= focus) a[focus - (n - 1 - enemycnt)] = i;
      }
    }
    for (int i = 0; i < (int)is_omg.size(); i++) {
      if (is_omg[i]) {
        int focus = i + 1;
        int cnt = 0;
        for (auto itr = nodes[focus].edges.begin();
             itr != nodes[focus].edges.end(); itr++) {
          if (itr->to > focus && itr->cap == 0) {
            cnt++;
            a[itr->to - (n - 1 - enemycnt)] = i;
          }
        }
        if (cnt == 1) {
          for (auto itr = nodes[focus].edges.begin();
               itr != nodes[focus].edges.end(); itr++) {
            if (itr->to > focus && itr->cap == 1 &&
                a[itr->to - (n - 1 - enemycnt)] != -1) {
              a[itr->to - (n - 1 - enemycnt)] = i;
              break;
            }
          }
        }
      }
    }
    int defeated = 0;
    for (int i = 0; i < (int)a.size(); i++) {
      if (a[i] != -1) defeated++;
    }
    cout << defeated << endl;
    for (int i = 0; i < (int)a.size(); i++) {
      if (a[i] != -1) cout << a[i] + 1 << " " << i + 1 << endl;
    }
  }
};
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  ;
  int n, m;
  cin >> n >> m;
  graph_t G;
  SegTree segtree(vector<int>(m, 0), 0, update_rule);
  G.n = 1 + n + (segtree.n * 2 - 1) + 1;
  for (int i = 0; i < (int)segtree.n - 1; i++) {
    G.edges.push_back({1 + n + i, 1 + n + segtree.left_of(i)});
    G.caps.push_back(segtree.n);
    G.edges.push_back({1 + n + i, 1 + n + segtree.right_of(i)});
    G.caps.push_back(segtree.n);
  }
  for (int i = 0; i < (int)segtree.n; i++) {
    G.edges.push_back({G.n - 1 - segtree.n + i, G.n - 1});
    G.caps.push_back(1);
  }
  vector<bool> is_omg(n, false);
  for (int i = 0; i < (int)n; i++) {
    int mode;
    cin >> mode;
    if (mode == 0) {
      G.edges.push_back({0, i + 1});
      G.caps.push_back(1);
      int setsize;
      cin >> setsize;
      for (int j = 0; j < (int)setsize; j++) {
        int t;
        cin >> t;
        --t;
        G.edges.push_back({i + 1, G.n - 1 - segtree.n + t});
        G.caps.push_back(1);
      }
    } else if (mode == 1) {
      G.edges.push_back({0, i + 1});
      G.caps.push_back(1);
      int s, t;
      cin >> s >> t;
      --s;
      --t;
      vector<int> indices = segtree.get_index_of_range(s, t + 1);
      for (int j = 0; j < (int)indices.size(); j++) {
        G.edges.push_back({i + 1, 1 + n + indices[j]});
        G.caps.push_back(1);
      }
    } else {
      G.edges.push_back({0, i + 1});
      G.caps.push_back(2);
      for (int j = 0; j < (int)3; j++) {
        int t;
        cin >> t;
        --t;
        G.edges.push_back({i + 1, G.n - 1 - segtree.n + t});
        G.caps.push_back(1);
      }
      is_omg[i] = true;
    }
  }
  G.m = G.edges.size();
  Maxflow maxflow(G, 0, G.n - 1);
  maxflow.solve(is_omg, segtree.n);
}
