pub mod fastin {
    use std::{io::Read, ptr::copy_nonoverlapping, slice::from_raw_parts_mut};

    const SIZE: usize = 1usize << 18;
    static mut BUFF: [u8; SIZE] = [0; SIZE];
    static mut PL: usize = 0;
    static mut PR: usize = 0;

    #[inline(always)]
    pub unsafe fn load(reader: &mut impl Read) {
        let head: *mut u8 = BUFF.as_mut_ptr();
        let src = head.add(PL);
        let count = PR - PL;
        copy_nonoverlapping(src, head, count);
        let buf = from_raw_parts_mut(head.add(count), SIZE - count);
        PR = count + reader.read(buf).unwrap();
        PL = 0;
    }
    #[inline(always)]
    unsafe fn readc() -> u8 {
        let c = BUFF[PL];
        PL += 1;
        c
    }
    #[inline(always)]
    pub unsafe fn read(reader: &mut impl Read) -> usize {
        if PL + 32 > PR {
            load(reader);
        }
        let mut c: u8 = readc();
        while c <= ' ' as u8 {
            c = readc();
        }
        let mut x = 0;
        while c >= '0' as u8 {
            x = x * 10 + (c & 15) as usize;
            c = readc();
        }
        x
    }
    #[inline(always)]
    pub unsafe fn readi(reader: &mut impl Read) -> isize {
        if PL + 32 > PR {
            load(reader);
        }
        let mut c: u8 = readc();
        let is_minus = c == '-' as u8;
        let mut x = 0;
        while c >= '0' as u8 {
            x = x * 10 + (c & 15) as isize;
            c = readc();
        }
        if is_minus {
            x = -x;
        }
        x
    }
}

pub mod fastout {
    use std::{io::Write, ptr::copy_nonoverlapping, slice::from_raw_parts};

    const SIZE: usize = 1usize << 18;
    static mut BUFF: [u8; SIZE] = [0; SIZE];
    static mut NUM: [u8; 40000] = [0; 40000];
    static mut PR: usize = 0;

    #[inline(always)]
    pub unsafe fn prepare() {
        for i in 0..10000 {
            let mut n = i;
            for j in (0..4).rev() {
                NUM[i * 4 + j] = (n % 10) as u8 + '0' as u8;
                n /= 10;
            }
        }
    }
    #[inline(always)]
    pub unsafe fn flush(writer: &mut impl Write) {
        let head: *const u8 = BUFF.as_ptr();
        let buf = from_raw_parts(head, PR);
        let _ = writer.write(buf);
        PR = 0;
    }
    #[inline(always)]
    pub unsafe fn writec(c: u8) {
        BUFF[PR] = c;
        PR += 1;
    }
    pub unsafe fn write(writer: &mut impl Write, x: usize) {
        if PR + 32 > SIZE {
            flush(writer);
        }
        let head: *mut u8 = BUFF.as_mut_ptr();
        let num: *const u8 = NUM.as_ptr();
        if x >= 1_0000_0000_0000_0000 {
            let r1 = x % 1_0000_0000;
            let q1 = x / 1_0000_0000;
            let r2 = q1 % 1_0000_0000;
            let n5 = q1 / 1_0000_0000;
            let n1 = r1 % 1_0000;
            let n2 = r1 / 1_0000;
            let n3 = r2 % 1_0000;
            let n4 = r2 / 1_0000;
            if x >= 100_0000_0000_0000_0000 {
                if x >= 1000_0000_0000_0000_0000 {
                    copy_nonoverlapping(num.add(n5 * 4), head.add(PR), 4);
                    copy_nonoverlapping(num.add(n4 * 4), head.add(PR + 4), 4);
                    copy_nonoverlapping(num.add(n3 * 4), head.add(PR + 8), 4);
                    copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 12), 4);
                    copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 16), 4);
                    PR += 20;
                } else {
                    copy_nonoverlapping(num.add(n5 * 4 + 1), head.add(PR), 3);
                    copy_nonoverlapping(num.add(n4 * 4), head.add(PR + 3), 4);
                    copy_nonoverlapping(num.add(n3 * 4), head.add(PR + 7), 4);
                    copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 11), 4);
                    copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 15), 4);
                    PR += 19;
                }
            } else {
                if x >= 10_0000_0000_0000_0000 {
                    let q3 = (n5 * 205) >> 11;
                    let r3 = n5 - q3 * 10;
                    BUFF[PR] = q3 as u8 + '0' as u8;
                    BUFF[PR + 1] = r3 as u8 + '0' as u8;
                    copy_nonoverlapping(num.add(n4 * 4), head.add(PR + 2), 4);
                    copy_nonoverlapping(num.add(n3 * 4), head.add(PR + 6), 4);
                    copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 10), 4);
                    copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 14), 4);
                    PR += 18;
                } else {
                    BUFF[PR] = n5 as u8 + '0' as u8;
                    copy_nonoverlapping(num.add(n4 * 4), head.add(PR + 1), 4);
                    copy_nonoverlapping(num.add(n3 * 4), head.add(PR + 5), 4);
                    copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 9), 4);
                    copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 13), 4);
                    PR += 17;
                }
            }
        } else if x >= 1_0000_0000 {
            if x >= 1_0000_0000_0000 {
                let r1 = x % 1_0000_0000;
                let q1 = x / 1_0000_0000;
                let n1 = r1 % 1_0000;
                let n2 = r1 / 1_0000;
                let n3 = q1 % 1_0000;
                let n4 = q1 / 1_0000;
                if x >= 100_0000_0000_0000 {
                    if x >= 1000_0000_0000_0000 {
                        copy_nonoverlapping(num.add(n4 * 4), head.add(PR), 4);
                        copy_nonoverlapping(num.add(n3 * 4), head.add(PR + 4), 4);
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 8), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 12), 4);
                        PR += 16;
                    } else {
                        copy_nonoverlapping(num.add(n4 * 4 + 1), head.add(PR), 3);
                        copy_nonoverlapping(num.add(n3 * 4), head.add(PR + 3), 4);
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 7), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 11), 4);
                        PR += 15;
                    }
                } else {
                    if x >= 10_0000_0000_0000 {
                        let q3 = (n4 * 205) >> 11;
                        let r3 = n4 - q3 * 10;
                        BUFF[PR] = q3 as u8 + '0' as u8;
                        BUFF[PR + 1] = r3 as u8 + '0' as u8;
                        copy_nonoverlapping(num.add(n3 * 4), head.add(PR + 2), 4);
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 6), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 10), 4);
                        PR += 14;
                    } else {
                        BUFF[PR] = n4 as u8 + '0' as u8;
                        copy_nonoverlapping(num.add(n3 * 4), head.add(PR + 1), 4);
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 5), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 9), 4);
                        PR += 13;
                    }
                }
            } else {
                let r1 = x % 1_0000_0000;
                let n1 = r1 % 1_0000;
                let n2 = r1 / 1_0000;
                let n3 = x / 1_0000_0000;
                if x >= 100_0000_0000 {
                    if x >= 1000_0000_0000 {
                        copy_nonoverlapping(num.add(n3 * 4), head.add(PR), 4);
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 4), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 8), 4);
                        PR += 12;
                    } else {
                        copy_nonoverlapping(num.add(n3 * 4 + 1), head.add(PR), 3);
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 3), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 7), 4);
                        PR += 11;
                    }
                } else {
                    if x >= 10_0000_0000 {
                        let q3 = (n3 * 205) >> 11;
                        let r3 = n3 - q3 * 10;
                        BUFF[PR] = q3 as u8 + '0' as u8;
                        BUFF[PR + 1] = r3 as u8 + '0' as u8;
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 2), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 6), 4);
                        PR += 10;
                    } else {
                        BUFF[PR] = n3 as u8 + '0' as u8;
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR + 1), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 5), 4);
                        PR += 9;
                    }
                }
            }
        } else {
            if x >= 1_0000 {
                let n1 = x % 1_0000;
                let n2 = x / 1_0000;
                if x >= 100_0000 {
                    if x >= 1000_0000 {
                        copy_nonoverlapping(num.add(n2 * 4), head.add(PR), 4);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 4), 4);
                        PR += 8;
                    } else {
                        copy_nonoverlapping(num.add(n2 * 4 + 1), head.add(PR), 3);
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 3), 4);
                        PR += 7;
                    }
                } else {
                    if x >= 10_0000 {
                        let q3 = (n2 * 205) >> 11;
                        let r3 = n2 - q3 * 10;
                        BUFF[PR] = q3 as u8 + '0' as u8;
                        BUFF[PR + 1] = r3 as u8 + '0' as u8;
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 2), 4);
                        PR += 6;
                    } else {
                        BUFF[PR] = n2 as u8 + '0' as u8;
                        copy_nonoverlapping(num.add(n1 * 4), head.add(PR + 1), 4);
                        PR += 5;
                    }
                }
            } else {
                if x >= 100 {
                    if x >= 1000 {
                        copy_nonoverlapping(num.add(x * 4), head.add(PR), 4);
                        PR += 4;
                    } else {
                        copy_nonoverlapping(num.add(x * 4 + 1), head.add(PR), 3);
                        PR += 3;
                    }
                } else {
                    if x >= 10 {
                        let q3 = (x * 205) >> 11;
                        let r3 = x - q3 * 10;
                        BUFF[PR] = q3 as u8 + '0' as u8;
                        BUFF[PR + 1] = r3 as u8 + '0' as u8;
                        PR += 2;
                    } else {
                        BUFF[PR] = x as u8 + '0' as u8;
                        PR += 1;
                    }
                }
            }
        }
    }
    pub unsafe fn write6(writer: &mut impl Write, x: u32) {
        if PR + 32 > SIZE {
            flush(writer);
        }
        let head: *mut u8 = BUFF.as_mut_ptr();
        let num: *const u8 = NUM.as_ptr();
        if x >= 1_0000 {
            let n1 = x % 1_0000;
            let n2 = x / 1_0000;
            if x >= 10_0000 {
                let q3 = (n2 * 205) >> 11;
                let r3 = n2 - q3 * 10;
                BUFF[PR] = q3 as u8 + '0' as u8;
                BUFF[PR + 1] = r3 as u8 + '0' as u8;
                copy_nonoverlapping(num.add(n1 as usize * 4), head.add(PR + 2), 4);
                PR += 6;
            } else {
                BUFF[PR] = n2 as u8 + '0' as u8;
                copy_nonoverlapping(num.add(n1 as usize * 4), head.add(PR + 1), 4);
                PR += 5;
            }
        } else {
            if x >= 100 {
                if x >= 1000 {
                    copy_nonoverlapping(num.add(x as usize * 4), head.add(PR), 4);
                    PR += 4;
                } else {
                    copy_nonoverlapping(num.add(x as usize * 4 + 1), head.add(PR), 3);
                    PR += 3;
                }
            } else {
                if x >= 10 {
                    let q3 = (x * 205) >> 11;
                    let r3 = x - q3 * 10;
                    BUFF[PR] = q3 as u8 + '0' as u8;
                    BUFF[PR + 1] = r3 as u8 + '0' as u8;
                    PR += 2;
                } else {
                    BUFF[PR] = x as u8 + '0' as u8;
                    PR += 1;
                }
            }
        }
    }
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct DirectedEdge;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct UndirectedEdge;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Adjacency {
    pub id: usize,
    pub to: usize,
}
impl Adjacency {
    pub fn new(id: usize, to: usize) -> Adjacency {
        Adjacency { id, to }
    }
}
/// Adjacency Graph Abstraction.
pub trait AdjacencyGraphAbstraction<'a> {
    type AdjIter: Iterator<Item = &'a Adjacency> + ExactSizeIterator;
    /// Return the number of vertices.
    fn vertices_size(&self) -> usize;
    /// Return the number of edges.
    fn edges_size(&self) -> usize;
    /// Return an iterator over graph vertices.
    fn vertices(&self) -> std::ops::Range<usize> {
        0..self.vertices_size()
    }
    /// Return a slice of adjacency vertices.
    fn adjacencies(&'a self, v: usize) -> Self::AdjIter;
    /// Construct graph from edges.
    fn from_edges(
        vsize: usize,
        edges: impl Iterator<Item = (usize, usize)> + ExactSizeIterator + Clone,
    ) -> Self;
}
/// Static Sparse Graph represented as Compressed Sparse Row.
#[derive(Debug, Clone)]
pub struct SparseGraph<D> {
    vsize: usize,
    pub(crate) start: Vec<usize>,
    pub(crate) elist: Vec<Adjacency>,
    _marker: std::marker::PhantomData<fn() -> D>,
}
impl<'a> AdjacencyGraphAbstraction<'a> for SparseGraph<DirectedEdge> {
    type AdjIter = std::slice::Iter<'a, Adjacency>;
    fn vertices_size(&self) -> usize {
        self.vsize
    }
    fn edges_size(&self) -> usize {
        self.elist.len()
    }
    fn adjacencies(&'a self, v: usize) -> Self::AdjIter {
        self.elist[self.start[v]..self.start[v + 1]].iter()
    }
    fn from_edges(
        vsize: usize,
        edges: impl Iterator<Item = (usize, usize)> + ExactSizeIterator + Clone,
    ) -> Self {
        let mut start = vec![0; vsize + 1];
        let mut elist = Vec::with_capacity(edges.len());
        unsafe { elist.set_len(edges.len()) }
        for (from, _) in edges.clone() {
            start[from] += 1;
        }
        for i in 1..=vsize {
            start[i] += start[i - 1];
        }
        for (id, (from, to)) in edges.enumerate() {
            start[from] -= 1;
            elist[start[from]] = Adjacency::new(id, to);
        }
        Self {
            vsize,
            start,
            elist,
            _marker: std::marker::PhantomData,
        }
    }
}
impl<'a> AdjacencyGraphAbstraction<'a> for SparseGraph<UndirectedEdge> {
    type AdjIter = std::slice::Iter<'a, Adjacency>;
    fn vertices_size(&self) -> usize {
        self.vsize
    }
    fn edges_size(&self) -> usize {
        self.elist.len() / 2
    }
    fn adjacencies(&'a self, v: usize) -> Self::AdjIter {
        self.elist[self.start[v]..self.start[v + 1]].iter()
    }
    fn from_edges(
        vsize: usize,
        edges: impl Iterator<Item = (usize, usize)> + ExactSizeIterator + Clone,
    ) -> Self {
        let mut start = vec![0; vsize + 1];
        let mut elist = Vec::with_capacity(edges.len() * 2);
        unsafe { elist.set_len(edges.len() * 2) }
        for (from, to) in edges.clone() {
            start[to] += 1;
            start[from] += 1;
        }
        for i in 1..=vsize {
            start[i] += start[i - 1];
        }
        for (id, (from, to)) in edges.enumerate() {
            start[from] -= 1;
            elist[start[from]] = Adjacency::new(id, to);
            start[to] -= 1;
            elist[start[to]] = Adjacency::new(id, from);
        }
        Self {
            vsize,
            start,
            elist,
            _marker: std::marker::PhantomData,
        }
    }
}
pub type DirectedSparseGraph = SparseGraph<DirectedEdge>;
pub type UndirectedSparseGraph = SparseGraph<UndirectedEdge>;

#[derive(Debug, Clone)]
pub struct StronglyConnectedComponent<'a> {
    graph: &'a DirectedSparseGraph,
    visited: Vec<usize>,
    csize: usize,
    low: Vec<usize>,
    ord: Vec<usize>,
    comp: Vec<usize>,
}
impl std::ops::Index<usize> for StronglyConnectedComponent<'_> {
    type Output = usize;
    fn index(&self, index: usize) -> &Self::Output {
        &self.comp[index]
    }
}
impl<'a> StronglyConnectedComponent<'a> {
    pub fn new(graph: &'a DirectedSparseGraph) -> Self {
        let mut now_ord = 0;
        let mut self_ = Self {
            graph,
            csize: 0,
            visited: Vec::with_capacity(graph.vertices_size()),
            low: vec![0; graph.vertices_size()],
            ord: vec![std::usize::MAX; graph.vertices_size()],
            comp: vec![0; graph.vertices_size()],
        };
        for u in graph.vertices() {
            if self_.ord[u] == std::usize::MAX {
                self_.dfs(u, &mut now_ord);
            }
        }
        for x in self_.comp.iter_mut() {
            *x = self_.csize - 1 - *x;
        }
        self_
    }
}
impl StronglyConnectedComponent<'_> {
    fn dfs(&mut self, u: usize, now_ord: &mut usize) {
        self.low[u] = *now_ord;
        self.ord[u] = *now_ord;
        *now_ord += 1;
        self.visited.push(u);
        for a in self.graph.adjacencies(u) {
            if self.ord[a.to] == std::usize::MAX {
                self.dfs(a.to, now_ord);
                self.low[u] = self.low[u].min(self.low[a.to]);
            } else {
                self.low[u] = self.low[u].min(self.ord[a.to]);
            }
        }
        if self.low[u] == self.ord[u] {
            while let Some(v) = self.visited.pop() {
                self.ord[v] = self.graph.vertices_size();
                self.comp[v] = self.csize;
                if v == u {
                    break;
                }
            }
            self.csize += 1;
        }
    }
    pub fn gen_cgraph(&self) -> DirectedSparseGraph {
        let mut used = std::collections::HashSet::new();
        let mut edges = vec![];
        for u in self.graph.vertices() {
            for a in self.graph.adjacencies(u) {
                if self.comp[u] != self.comp[a.to] {
                    let (x, y) = (self.comp[u], self.comp[a.to]);
                    if !used.contains(&(x, y)) {
                        used.insert((x, y));
                        edges.push((x, y));
                    }
                }
            }
        }
        DirectedSparseGraph::from_edges(self.size(), edges.iter().cloned())
    }
    pub fn components(&self) -> Vec<Vec<usize>> {
        let mut counts = vec![0; self.size()];
        for &x in self.comp.iter() {
            counts[x] += 1;
        }
        let mut groups = vec![vec![]; self.size()];
        for (g, c) in groups.iter_mut().zip(counts.into_iter()) {
            g.reserve(c);
        }
        for u in self.graph.vertices() {
            groups[self[u]].push(u);
        }
        groups
    }
    pub fn has_loop(&self) -> bool {
        self.graph.vertices_size() != self.csize
    }
    pub fn size(&self) -> usize {
        self.csize
    }
}

unsafe fn solve(reader: &mut impl std::io::Read, writer: &mut impl std::io::Write) {
    fastin::load(reader);
    fastout::prepare();
    let vs: usize = fastin::read(reader);
    let es: usize = fastin::read(reader);
    let mut edges = Vec::with_capacity(es);
    for _ in 0..es {
        let u = fastin::read(reader);
        let v = fastin::read(reader);
        edges.push((u, v));
    }
    let graph = DirectedSparseGraph::from_edges(vs, edges.iter().cloned());
    let scc = StronglyConnectedComponent::new(&graph);
    let comp = scc.components();
    fastout::write6(writer, comp.len() as u32);
    fastout::writec('\n' as u8);
    for vs in comp {
        fastout::write6(writer, vs.len() as u32);
        for v in vs {
            fastout::writec(' ' as u8);
            fastout::write6(writer, v as u32);
        }
        fastout::writec('\n' as u8);
    }
    fastout::flush(writer);
}

fn main() {
    let (stdin, stdout) = (std::io::stdin(), std::io::stdout());
    let (mut reader, mut writer) = (stdin.lock(), stdout.lock());
    unsafe {
        fastin::load(&mut reader);
        fastout::prepare();
        solve(&mut reader, &mut writer);
        fastout::flush(&mut writer);
    }
}

Measured memory_bytes: 64832