import java.util.List;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.io.BufferedReader;
import java.util.Comparator;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.util.ArrayDeque;
import java.io.PrintWriter;
import java.io.FileInputStream;
import java.util.StringTokenizer;
import java.util.HashSet;
import java.util.Collections;
import java.io.InputStream;
 
/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class cactus_petr_wa {
    public static void main(String[] args) {
        InputStream inputStream;
        try {
            inputStream = new FileInputStream("cactus.in");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        OutputStream outputStream;
        try {
            outputStream = new FileOutputStream("cactus.out");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        InputReader_petr_wa in = new InputReader_petr_wa(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        Cactus_petr_wa_0 solver = new Cactus_petr_wa_0();
        solver.solve(1, in, out);
        out.close();
    }
}
 
class Cactus_petr_wa_0 {
 
    static final long BASE = 3137;
 
    static List<Node> dfsStack;
    static List<List<Node>> cycles;
    static long[] fact;
    static long[] pow;
 
    static class Node {
        int index;
        int kind = 0;
        int inStack = -1;
        int depth = -1;
        int remAdj;
        HashSet<Node> adj = new HashSet<Node>();
        List<Node> cycle = null;
        long hashKey;
        long hashKeyPow;
 
        public void findCycles(Node parent) {
            inStack = dfsStack.size();
            dfsStack.add(this);
 
            for (Node other : adj) {
                if (other == parent) continue;
                if (other.inStack >= 0) {
                    List<Node> cycle = new ArrayList<Node>();
                    for (int i = other.inStack; i <= inStack; ++i)
                        cycle.add(dfsStack.get(i));
                    cycles.add(cycle);
                } else if (other.inStack == -1) {
                    other.findCycles(this);
                }
            }
 
            dfsStack.remove(inStack);
            inStack = -2;
        }
 
        public void solve(Node parent) {
            List<Node> children = new ArrayList<Node>();
            hashKey = kind + 1;
            hashKeyPow = BASE;
            if (kind == 0) {
                for (Node child : adj) if (child != parent) {
                    child.solve(this);
                    children.add(child);
                }
                Collections.sort(children, new Comparator<Node>() {
                    @Override
                    public int compare(Node o1, Node o2) {
                        return Long.compare(o1.hashKey, o2.hashKey);
                    }
                });
                int cnt = 0;
                long lastHashKey = -1;
                for (Node child : children) {
                    hashKey = hashKey * child.hashKeyPow + child.hashKey;
                    hashKeyPow = hashKeyPow * child.hashKeyPow;
                    if (child.hashKey == lastHashKey) ++cnt; else {
                        ++fact[cnt];
                        cnt = 1;
                        lastHashKey = child.hashKey;
                    }
                }
                ++fact[cnt];
            } else if (parent == null) {
                long[] seq = new long[cycle.size()];
                int[] kmp = new int[cycle.size() + 1];
                for (int i = 0; i < cycle.size(); ++i) {
                    cycle.get(i).solve(this);
                    seq[i] = cycle.get(i).hashKey;
                }
                for (int i = 2; i < kmp.length; ++i) {
                    int j = kmp[i - 1];
                    while (true) {
                        if (seq[j] == seq[i - 1]) {
                            ++j;
                            break;
                        }
                        if (j == 0) break;
                        j = kmp[j];
                    }
                    kmp[i] = j;
                }
                int period = -1;
                {
                    int j = 0;
                    for (int i = 1; i < 2 * cycle.size(); ++i) {
                        long need = seq[i % cycle.size()];
                        while (true) {
                            if (seq[j] == need) {
                                ++j;
                                break;
                            }
                            if (j == 0) break;
                            j = kmp[j];
                        }
                        if (j == cycle.size()) {
                            period = i + 1 - cycle.size();
                            break;
                        }
                    }
                }
                if (period < 0 || cycle.size() % period != 0) throw new RuntimeException();
                int autos = cycle.size() / period;
                if (cycle.size() > 2) {
                    int j = 0;
                    for (int i = 0; i < 2 * cycle.size(); ++i) {
                        long need = seq[(2 * cycle.size() - i) % cycle.size()];
                        while (true) {
                            if (seq[j] == need) {
                                ++j;
                                break;
                            }
                            if (j == 0) break;
                            j = kmp[j];
                        }
                        if (j == cycle.size()) {
                            autos *= 2;
                            break;
                        }
                    }
                }
                ++pow[autos];
            } else {
                int parentPos = -1;
                for (int i = 0; i < cycle.size(); ++i)
                    if (cycle.get(i) == parent) parentPos = i;
                if (parentPos < 0) throw new RuntimeException();
                for (int i = 1; i < cycle.size(); ++i) {
                    Node child = cycle.get((parentPos + i) % cycle.size());
                    child.solve(this);
                    children.add(child);
                }
                if (cycle.size() > 2) {
                    int cmp = 0;
                    for (int i = 0; i < children.size(); ++i) {
                        int z = Long.compare(children.get(i).hashKey, children.get(children.size() - 1 - i).hashKey);
                        if (z != 0) {
                            cmp = z;
                            break;
                        }
                    }
                    if (cmp == 0)
                        ++pow[2];
                    if (cmp == 1) {
                        Collections.reverse(children);
                    }
                }
                for (Node child : children) {
                    hashKey = hashKey * child.hashKeyPow + child.hashKey;
                    hashKeyPow = hashKeyPow * child.hashKeyPow;
                }
            }
            /*hashKey = hashKey * BASE + 57;
            hashKeyPow *= BASE;*/
            hashKeyPow = BASE;
        }
    }
 
    public void solve(int testNumber, InputReader_petr_wa in, PrintWriter out) {
        int n = in.nextInt();
        int m = in.nextInt();
        if (n == 1) {
            out.println(0);
            return;
        }
        Node[] nodes = new Node[n];
        for (int i = 0; i < n; ++i) {
            nodes[i] = new Node();
            nodes[i].index = i + 1;
        }
        for (int i = 0; i < m; ++i) {
            int len = in.nextInt();
            Node at = nodes[in.nextInt() - 1];
            for (int j = 1; j < len; ++j) {
                Node next = nodes[in.nextInt() - 1];
                at.adj.add(next);
                next.adj.add(at);
                at = next;
            }
        }
        dfsStack = new ArrayList<Node>();
        cycles = new ArrayList<List<Node>>();
        fact = new long[100001];
        pow = new long[100001];
        nodes[0].findCycles(null);
        List<Node> allNodes = new ArrayList<Node>();
        for (Node nn : nodes) allNodes.add(nn);
        for (List<Node> cycle : cycles) {
            Node cycleNode = new Node();
            cycleNode.kind = 1;
            cycleNode.cycle = cycle;
            for (int i = 0; i < cycle.size(); ++i) {
                Node cur = cycle.get(i);
                Node next = cycle.get((i + 1) % cycle.size());
                cur.adj.remove(next);
                next.adj.remove(cur);
                cur.adj.add(cycleNode);
                cycleNode.adj.add(cur);
            }
            allNodes.add(cycleNode);
        }
        ArrayDeque<Node> queue = new ArrayDeque<Node>();
        for (Node node : allNodes) node.remAdj = node.adj.size();
        for (Node node : allNodes) if (node.adj.size() == 1) {
            node.depth = 0;
            queue.addLast(node);
        }
        while (!queue.isEmpty()) {
            Node cur = queue.pollFirst();
            for (Node nn : cur.adj) {
                if (nn.depth < 0) {
                    --nn.remAdj;
                    if (nn.remAdj == 1) {
                        nn.depth = cur.depth + 1;
                        queue.addLast(nn);
                    }
                }
            }
        }
        int maxDepth = -1;
        for (Node node : allNodes) maxDepth = Math.max(maxDepth, node.depth);
        List<Node> maxes = new ArrayList<Node>();
        for (Node node : allNodes) if (node.depth == maxDepth) maxes.add(node);
        if (maxes.size() == 2) {
            if (maxes.get(0).kind != maxes.get(1).kind) {
                if (maxes.get(0).kind == 1)
                    maxes.remove(1);
                else
                    maxes.remove(0);
            } else {
                Node megaroot = new Node();
                megaroot.kind = 0;
                maxes.get(0).adj.remove(maxes.get(1));
                maxes.get(1).adj.remove(maxes.get(0));
                maxes.get(0).adj.add(megaroot);
                megaroot.adj.add(maxes.get(0));
                maxes.get(1).adj.add(megaroot);
                megaroot.adj.add(maxes.get(1));
                maxes.clear();
                maxes.add(megaroot);
            }
        }
        if (maxes.size() != 1) throw new RuntimeException();
        Node root = maxes.get(0);
        root.solve(null);
        long factsum = 0;
        for (int i = fact.length - 1; i >= 0; --i) {
            factsum += fact[i];
            pow[i] += factsum;
        }
        int[] divisor = new int[pow.length];
        for (int i = 2; i < divisor.length; ++i)
            if (divisor[i] == 0) {
                for (int j = 2 * i; j < divisor.length; j += i)
                    divisor[j] = i;
            }
        for (int i = pow.length - 1; i >= 0; --i) {
            if (divisor[i] != 0) {
                pow[divisor[i]] += pow[i];
                pow[i / divisor[i]] += pow[i];
                pow[i] = 0;
            }
        }
        int cnt = 0;
        for (int i = 2; i < pow.length; ++i) if (pow[i] > 0) ++cnt;
        out.println(cnt);
        for (int i = 2; i < pow.length; ++i) if (pow[i] > 0) out.println(i + " " + pow[i]);
    }
}
 
class InputReader_petr_wa {
    public BufferedReader reader;
    public StringTokenizer tokenizer;
 
    public InputReader_petr_wa(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream));
        tokenizer = null;
    }
 
    public String next() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }
 
    public int nextInt() {
        return Integer.parseInt(next());
    }
 
    }