#include <bits/stdc++.h>   using namespace std;   template <typename T>   inline bool chkmin(T &a, T b) {     return b < a ? a = b, 1 : 0;   }   template <typename T>   inline bool chkmax(T &a, T b) {     return b > a ? a = b, 1 : 0;   }   inline int read() {     int x(0), sgn(1);     char ch(getchar());     for (; !isdigit(ch); ch = getchar())       if (ch ==  - ) sgn = -1;     for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);     return x * sgn;   }   void File() {}   int n, m;   const int inf = 0x3f3f3f3f;   template <int Maxn, int Maxm>   struct Dinic {     int Head[Maxn], Next[Maxm], cap[Maxm], to[Maxm], e;     Dinic() { e = 1; }     inline void add_edge(int u, int v, int flow) {       to[++e] = v;       Next[e] = Head[u];       Head[u] = e;       cap[e] = flow;     }     inline void Add(int u, int v, int flow) {       add_edge(u, v, flow);       add_edge(v, u, 0);     }     int S, T, dis[Maxn];     bool Bfs() {       queue<int> Q;       Q.push(S);       memset(dis, 0, sizeof(dis));       dis[S] = 1;       while (!Q.empty()) {         int u = Q.front();         Q.pop();         for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]])           if (cap[i] && !dis[v]) dis[v] = dis[u] + 1, Q.push(v);       }       return dis[T];     }     int cur[Maxn];     int Dfs(int u, int flow) {       if (u == T || !flow) return flow;       int res = 0, f;       for (int &i = cur[u]; i; i = Next[i]) {         int v = to[i];         if (dis[v] == dis[u] + 1 && (f = Dfs(v, min(flow, cap[i])))) {           cap[i] -= f, cap[i ^ 1] += f, res += f;           if (!(flow -= f)) break;         }       }       if (!res) dis[u] = 0;       return res;     }     int Run() {       int sum_flow = 0;       while (Bfs()) memcpy(cur, Head, sizeof(cur)), sum_flow += Dfs(S, inf);       return sum_flow;     }   };   const int N = 5050;   Dinic<(int)1e5, (int)4e5> D;   int tot;   int ans[N], Bef;   template <int Maxn>   struct Segment_Tree {     int id[Maxn];     void Build(int o, int l, int r) {       if (l == r) {         id[o] = l;         return;       }       id[o] = ++tot;       int mid = (l + r) >> 1;       Build(o << 1, l, mid);       Build(o << 1 | 1, mid + 1, r);       D.Add(id[o], id[o << 1], id[o << 1] <= m ? 1 : inf);       D.Add(id[o], id[o << 1 | 1], id[o << 1 | 1] <= m ? 1 : inf);     }     inline void Connect(int o, int l, int r, int ql, int qr, int Id) {       if (ql <= l && r <= qr) {         D.Add(Id, id[o], 2);         return;       }       int mid = (l + r) >> 1;       if (ql <= mid) Connect(o << 1, l, mid, ql, qr, Id);       if (qr > mid) Connect(o << 1 | 1, mid + 1, r, ql, qr, Id);     }     vector<int> find(int o, int l, int r) {       if (l == r) return vector<int>();       vector<int> tmp;       int mid = (l + r) >> 1;       vector<int> ls = find(o << 1, l, mid), rs = find(o << 1 | 1, mid + 1, r);       set_union((ls).begin(), (ls).end(), (rs).begin(), (rs).end(),                 inserter(tmp, tmp.end()));       for (int i = D.Head[id[o]], v = D.to[i]; i; v = D.to[i = D.Next[i]])         if (v <= m && !D.cap[i]) tmp.push_back(v);       for (int i = D.Head[id[o]], v = D.to[i]; i; v = D.to[i = D.Next[i]])         if (D.cap[i] && v > Bef) {           int cur = tmp[tmp.size() - 1];           tmp.pop_back();           ans[cur] = v - Bef;         }       return tmp;     }   };   Segment_Tree<N << 2> T;   vector<int> V1, V2, V3;   int Ref[N], from[N], go[N];   void Get_Ans() {     for (int u : V1) {       for (int i = D.Head[Ref[u]], v = D.to[i]; i; v = D.to[i = D.Next[i]])         if (v <= m && D.cap[i ^ 1]) ans[v] = u;     }     T.find(1, 1, m);     for (int u : V3) {       int flow = D.cap[from[u]];       for (int i = D.Head[Ref[u]], v = D.to[i]; i; v = D.to[i = D.Next[i]])         if (v != D.S && !D.cap[i]) ans[v] = u;       if (flow == 1)         for (int i = D.Head[Ref[u]], v = D.to[i]; i; v = D.to[i = D.Next[i]])           if (v != D.S && D.cap[i]) {             ans[v] = u;             break;           }     }   }   int main() {     File();     n = read(), m = read();     tot = m;     T.Build(1, 1, m);     Bef = tot;     D.S = tot + n + 1;     D.T = tot + n + 2;     for (register int i = (1), iend = (int)(m); i <= iend; ++i)       D.Add(i, D.T, 1), go[i] = D.e - 1;     for (register int i = (1), iend = (int)(n); i <= iend; ++i) {       int opt = read();       D.Add(D.S, Ref[i] = ++tot, 1 + (opt == 2));       from[i] = D.e - 1;       if (opt == 0) {         int k = read();         V1.push_back(i);         while (k--) D.Add(tot, read(), 1);       } else if (opt == 1) {         int l = read(), r = read();         V1.push_back(i);         T.Connect(1, 1, m, l, r, tot);       } else {         int a = read(), b = read(), c = read();         V3.push_back(i);         D.Add(tot, a, 1);         D.Add(tot, b, 1);         D.Add(tot, c, 1);       }     }     printf( %d n , D.Run());     Get_Ans();     for (register int i = (1), iend = (int)(m); i <= iend; ++i)       if (ans[i]) printf( %d %d n , ans[i], i);     return 0;   }