Problem p02564 (C++)

Buggy submission (status: Runtime Error):
#define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
#define int long long
#define ll long long
using ull = unsigned long long;
using namespace std;
#define dump(x)                                                                \
  if (dbg) {                                                                   \
    cerr << #x << " = " << (x) << endl;                                        \
  }
#define overload4(_1, _2, _3, _4, name, ...) name
#define FOR1(n) for (ll i = 0; i < (n); ++i)
#define FOR2(i, n) for (ll i = 0; i < (n); ++i)
#define FOR3(i, a, b) for (ll i = (a); i < (b); ++i)
#define FOR4(i, a, b, c) for (ll i = (a); i < (b); i += (c))
#define FOR(...) overload4(__VA_ARGS__, FOR4, FOR3, FOR2, FOR1)(__VA_ARGS__)
#define FORR(i, a, b) for (int i = (a); i <= (b); ++i)
#define bit(n, k) (((n) >> (k)) & 1) /*nのk bit目*/
namespace mydef {
const int INF = 1ll << 60;
const int MOD = 1e9 + 7;
template <class T> bool chmin(T &a, const T &b) {
  if (a > b) {
    a = b;
    return 1;
  } else
    return 0;
}
template <class T> bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return 1;
  } else
    return 0;
}
void Yes(bool flag = true) {
  if (flag)
    cout << "Yes" << endl;
  else
    cout << "No" << endl;
}
void No(bool flag = true) { Yes(!flag); }
void YES(bool flag = true) {
  if (flag)
    cout << "YES" << endl;
  else
    cout << "NO" << endl;
}
void NO(bool flag = true) { YES(!flag); }
template <typename A, size_t N, typename T>
void Fill(A (&array)[N], const T &val) {
  std::fill((T *)array, (T *)(array + N), val);
}
bool dbg = true;
} // namespace mydef
using namespace mydef;
#define pb push_back
// #define mp make_pair
#define eb emplace_back
#define lb lower_bound
#define ub upper_bound
#define all(v) (v).begin(), (v).end()
#define SZ(x) ((int)(x).size())
#define vi vector<int>
#define vvi vector<vector<int>>
#define vp vector<pair<int, int>>
#define vvp vector<vector<pair<int, int>>>
#define pi pair<int, int>
// #define P pair<int, int>
// #define V vector<int>
// #define S set<int>
#define asn ans

#include <bits/stdc++.h>
using namespace std;
template <typename T> struct edge {
  int to, id;
  T cost;
  edge(int to) : to(to), id(-1), cost(1) {}
  edge(int to, T cost) : to(to), id(-1), cost(cost) {}
  edge(int to, T cost, int id) : to(to), id(id), cost(cost) {}
  operator int() const { return to; }
  /*
      edge& operator=(const int& x) {
          to = x;
          return *this;
      }
      */
};
template <typename T> class Graph : public vector<vector<edge<T>>> {
  //--------Basic--------
private:
  using Edge = edge<T>;
  int M; // M:辺の数
  bool undirected = false;
  bool directed = false;
  bool unweighted = false;
  bool weighted = false;
  bool tree = false;
  //
public:
  vector<int> A, B;
  vector<T> C;
  //
public:
  Graph() = default;
  // unweighted
  void add_edge_undirected(int u, int v, int id = -1) {
    assert(!directed);
    assert(!weighted);
    undirected = true;
    unweighted = true;
    (*this)[u].emplace_back(Edge{v, 1, id});
    (*this)[v].emplace_back(Edge{u, 1, id});
  }
  void build_undirected(int m) {
    assert(!(*this).empty());
    assert(!directed);
    assert(!weighted);
    undirected = true;
    unweighted = true;
    M = m;
    A.resize(m);
    B.resize(m);
    for (int i = 0; i < m; i++) {
      int u, v;
      cin >> u >> v;
      u--;
      v--;
      A[i] = u;
      B[i] = v;
      (*this)[u].emplace_back(Edge{v, 1, i});
      (*this)[v].emplace_back(Edge{u, 1, i});
    }
  }
  void build_undirected(int n, int m) {
    (*this).resize(n);
    build_undirected(m);
  }
  void add_edge_directed(int u, int v, int id = -1) {
    assert(!undirected);
    assert(!weighted);
    directed = true;
    unweighted = true;
    (*this)[u].emplace_back(Edge{v, 1, id});
  }
  void build_directed(int m) {
    assert(!(*this).empty());
    assert(!undirected);
    assert(!weighted);
    directed = true;
    unweighted = true;
    M = m;
    A.resize(M);
    B.resize(M);
    for (int i = 0; i < m; i++) {
      int u, v;
      cin >> u >> v;
      u--;
      v--;
      A[i] = u;
      B[i] = v;
      (*this)[u].emplace_back(Edge{v, 1, i});
    }
  }
  void build_directed(int n, int m) {
    (*this).resize(n);
    build_directed(m);
  }
  // weighed
  void add_edge_undirected_weighed(int u, int v, T cost, int id = -1) {
    assert(!directed);
    assert(!unweighted);
    undirected = true;
    weighted = true;
    (*this)[u].emplace_back(Edge{v, cost, id});
    (*this)[v].emplace_back(Edge{u, cost, id});
  }
  void build_undirected_weighted(int m) {
    assert(!(*this).empty());
    assert(!directed);
    assert(!unweighted);
    undirected = true;
    weighted = true;
    M = m;
    A.resize(m);
    B.resize(m);
    C.resize(m);
    for (int i = 0; i < m; i++) {
      int u, v;
      T cost;
      cin >> u >> v >> cost;
      u--;
      v--;
      A[i] = u;
      B[i] = v;
      C[i] = cost;
      (*this)[u].emplace_back(Edge{v, cost, i});
      (*this)[v].emplace_back(Edge{u, cost, i});
    }
  }
  void build_undirected_weighted(int n, int m) {
    (*this).resize(n);
    build_undirected_weighted(m);
  }
  void add_edge_directed_weighted(int u, int v, T cost, int id = -1) {
    assert(!undirected);
    assert(!unweighted);
    directed = true;
    weighted = true;
    (*this)[u].emplace_back(Edge{v, cost, id});
  }
  void build_directed_weighted(int m) {
    assert(!(*this).empty());
    assert(!undirected);
    assert(!unweighted);
    directed = true;
    weighted = true;
    M = m;
    A.resize(m);
    B.resize(m);
    C.resize(m);
    for (int i = 0; i < m; i++) {
      int u, v;
      T cost;
      cin >> u >> v >> cost;
      u--;
      v--;
      A[i] = u;
      B[i] = v;
      C[i] = cost;
      (*this)[u].emplace_back(Edge{v, cost, i});
    }
  }
  void build_directed_weighted(int n, int m) {
    (*this).resize(n);
    build_directed_weighted(m);
  }
  void build(vector<vector<int>> G) {
    int N = G.size();
    (*this).resize(N);
    for (int i = 0; i < N; i++) {
      for (auto &x : G[i]) {
        add_edge_directed(i, x);
      }
    }
  }
  // tree
  void istree() { tree = true; }
  void build_tree(int n) {
    istree();
    build_undirected(n, n - 1);
  }
  void build_tree_weighted(int n) {
    istree();
    build_undirected_weighted(n, n - 1);
  }
  // print state
  void state() {
    cerr << endl
         << "Print State" << endl
         << "Edge :" << endl
         << "    Directed   : " << (directed ? "Yes" : "No") << endl
         << "    Undirected : " << (undirected ? "Yes" : "No") << endl
         << "    Weighted   : " << (weighted ? "Yes" : "No") << endl
         << "    Unweighted : " << (unweighted ? "Yes" : "No") << endl
         << "    Tree       : " << (tree ? "Yes" : "No") << endl
         << "Test" << endl
         << "    test_bipartite(verified) : Yes" << endl
         << "Usable Functions : Graph" << endl
         << "    Dijkstra(verified) : Yes" << endl
         << "    Warshall–Floyd(verified) : Yes" << endl
         << "    Kruskal(verified) : Yes" << endl
         << "    TopologicalSort(verified) : Yes" << endl
         << "    StronglyConnectedComponent(unverified): "
         << (StronglyConnectedComponent_ready ? "Yes" : "No") << endl
         << "Usable Funcitons : Tree" << endl
         << "    ReRooting(verified) : Yes" << endl;
  }
  //
  //--------union-find tree--------
  struct UnionFind {
    vector<int> data;
    UnionFind(int n) { data.assign(n, -1); }
    bool unite(int x, int y) {
      x = root(x), y = root(y);
      if (x == y)
        return (false);
      if (data[x] > data[y])
        swap(x, y);
      data[x] += data[y];
      data[y] = x;
      return (true);
    }
    int root(int k) {
      if (data[k] < 0)
        return (k);
      return (data[k] = root(data[k]));
    }
    bool same(int x, int y) { return root(x) == root(y); }
    int size(int k) { return (-data[root(k)]); }
  };
  //
  //--------test_bipartite--------
  // verified
public:
  bool test_bipartite() {
    int N = (*this).size();
    UnionFind uf(N * 2);
    for (int i = 0; i < N; i++) {
      for (auto &e : (*this)[i]) {
        uf.unite(i, e.to + N);
        uf.unite(i + N, e.to);
      }
    }
    for (int i = 0; i < N; i++) {
      if (uf.same(i, i + N))
        return false;
    }
    return true;
  }
  //
  //--------reverse--------
public:
  void reverse() {
    int N = (*this).size();
    vector<pair<int, Edge>> V;
    V.reserve(M);
    for (int i = 0; i < N; i++) {
      for (auto &e : (*this)[i]) {
        V.emplace_back(make_pair(i, e));
      }
    }
    (*this).erase((*this).begin(), (*this).end());
    (*this).resize(N);
    for (auto &p : V)
      (*this)[p.second.to].emplace_back(
          Edge{p.first, p.second.cost, p.second.id});
  }
  //
  //--------Dijkstra--------
  // verified
public:
  // pair<vector<T>, vector<int>> Dijkstra(int s) {
  vector<T> Dijkstra(int s) {
    const T INF = numeric_limits<T>::max() / 5;
    using P = pair<T, int>;
    int N = (*this).size();
    vector<T> dist(N, INF);
    // vector<int> bef(N, -1);
    priority_queue<P, vector<P>, greater<P>> que;
    dist[s] = 0;
    que.emplace(dist[s], s);
    while (!que.empty()) {
      P p = que.top();
      que.pop();
      int now = p.second;
      if (dist[now] < p.first)
        continue;
      for (auto &p : (*this)[now]) {
        int nxt = p.to;
        T cost = p.cost;
        if (dist[nxt] > dist[now] + cost) {
          dist[nxt] = dist[now] + cost;
          // bef[nxt] = now;
          que.emplace(dist[nxt], nxt);
        }
      }
    }
    return dist;
    // return make_pair(dist, bef);
  }
  //
  //
  //--------Warshall–Floyd--------
  // verified
public:
  vector<vector<T>> WarshallFloyd() {
    int N = (*this).size();
    const T INF = numeric_limits<T>::max() / 3;
    vector<vector<T>> ret(N, vector<T>(N, INF));
    for (int i = 0; i < N; i++) {
      for (auto &e : (*this)[i]) {
        ret[i][e.to] = e.cost;
      }
      ret[i][i] = 0;
    }
    for (int k = 0; k < N; k++) {
      for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
          if (ret[i][j] > ret[i][k] + ret[k][j])
            ret[i][j] = ret[i][k] + ret[k][j];
        }
      }
    }
    return ret;
  }
  //
  //
  //--------Kruskal--------
  // verified
private:
  vector<int> Kruskal_data;
  bool Kruskal_unite(int x, int y) {
    x = Kruskal_root(x), y = Kruskal_root(y);
    if (x == y)
      return (false);
    if (Kruskal_data[x] > Kruskal_data[y])
      swap(x, y);
    Kruskal_data[x] += Kruskal_data[y];
    Kruskal_data[y] = x;
    return (true);
  }
  int Kruskal_root(int k) {
    if (Kruskal_data[k] < 0)
      return (k);
    return (Kruskal_data[k] = Kruskal_root(Kruskal_data[k]));
  }
  bool Kruskal_same(int x, int y) { return Kruskal_root(x) == Kruskal_root(y); }
  int Kruskal_size(int k) { return (-Kruskal_data[Kruskal_root(k)]); }
  //
public:
  template <class Compare = less<T>>
  pair<T, vector<bool>> Kruskal(bool flag = false) {
    struct edge2 {
      int from, to;
      T cost;
      bool used;
      int id;
      edge2(int from, int to, T cost, int id)
          : from(from), to(to), cost(cost), used(false), id(id) {}
    };
    vector<edge2> edges;
    int N = (*this).size();
    Kruskal_data.assign(N, -1);
    for (int i = 0; i < (int)(*this).size(); i++) {
      auto &V = (*this)[i];
      for (auto &e : V) {
        edges.emplace_back(i, e.to, e.cost, e.id);
      }
    }
    sort(edges.begin(), edges.end(), [](const edge2 &a, const edge2 &b) {
      return Compare()(a.cost, b.cost);
    });
    T ret = 0;
    vector<bool> V;
    if (flag)
      V.resize(M, false);
    for (auto &e : edges) {
      if (Kruskal_unite(e.from, e.to)) {
        ret += e.cost;
        e.used = true;
      }
    }
    if (flag)
      for (auto &e : edges) {
        assert(e.id >= 0 && e.id < M);
        if (e.used)
          V[e.id] = true;
      }
    if (N == Kruskal_size(0))
      return make_pair(ret, V);
    else
      return make_pair(-1, V);
  }
  //
  //--------StronglyConnectedComponent--------
  // Unverified
public:
  vector<vector<int>> SCC_R, SCC_T;
  //
private:
  bool StronglyConnectedComponent_ready = false;
  vector<int> SCC_cmp, SCC_ord;
  vector<bool> SCC_used;
  //
public:
  int Gbuild_SCC() {
    cerr << "Please Verify" << endl;
    assert(!undirected);
    assert(directed);
    int N = (*this).size();
    SCC_R.resize(N);
    for (int i = 0; i < N; i++) {
      for (auto &x : (*this)[i]) {
        SCC_R[x].emplace_back(i);
      }
    }
    SCC_cmp.resize(N);
    fill(SCC_cmp.begin(), SCC_cmp.end(), -1);
    SCC_used.resize(N);
    fill(SCC_used.begin(), SCC_used.end(), false);
    StronglyConnectedComponent_ready = true;
    return build_StronglyConnectedComponent_init();
  }
  int SCC(int k) {
    assert(StronglyConnectedComponent_ready);
    return SCC_cmp[k];
  }
  //
private:
  void build_StronglyConnectedComponent_dfs(int now) {
    if (SCC_used[now])
      return;
    SCC_used[now] = true;
    for (auto nxt : (*this)[now])
      build_StronglyConnectedComponent_dfs(nxt);
    SCC_ord.emplace_back(now);
  }
  void build_StronglyConnectedComponent_rdfs(int now, int count) {
    if (SCC_cmp[now] != -1)
      return;
    SCC_cmp[now] = count;
    for (auto to : SCC_R[now])
      build_StronglyConnectedComponent_rdfs(to, count);
  }
  int build_StronglyConnectedComponent_init() {
    int n = (int)(*this).size();
    for (int i = 0; i < n; i++)
      build_StronglyConnectedComponent_dfs(i);
    std::reverse(SCC_ord.begin(), SCC_ord.end());
    int group = 0;
    for (auto &i : SCC_ord) {
      if (SCC_cmp[i] == -1) {
        build_StronglyConnectedComponent_rdfs(i, group);
        group++;
      }
    }
    SCC_T.resize(group);
    for (int i = 0; i < n; i++) {
      for (auto &to : (*this)[i]) {
        int s = SCC_cmp[i], t = SCC_cmp[to];
        if (s != t)
          SCC_T[s].emplace_back(t);
      }
    }
    return group;
  }
  //
  //
  //
  //--------TopologicalSort--------
  // verified
public:
  vector<int> TopologicalSort() {
    assert(!undirected);
    int N = (*this).size();
    vector<int> deg(N);
    for (auto &V : (*this))
      for (auto &e : V)
        deg[e.to]++;
    stack<int> st;
    for (int i = 0; i < N; i++)
      if (deg[i] == 0)
        st.emplace(i);
    vector<int> ret;
    ret.reserve(N);
    while (!st.empty()) {
      auto x = st.top();
      st.pop();
      ret.emplace_back(x);
      for (auto &e : (*this)[x])
        if (--deg[e.to] == 0)
          st.emplace(e.to);
    }
    return ret;
  }
  //
  //
  //--------Tree--------
  //
  //
  //--------ReRooting--------
public:
  template <typename sum_t>
  pair<vector<sum_t>, vector<vector<sum_t>>>
  Tbuild_ReRooting(const function<sum_t(sum_t, sum_t)> f,
                   const function<sum_t(sum_t, Edge)> gg, sum_t ident) {
    assert(tree);
    int N = (*this).size();
    vector<sum_t> subdp(N, ident), dp(N, ident);
    vector<vector<sum_t>> g_dp(N), g_ndp(N), memo(N);
    vector<vector<bool>> seen(N);
    for (int i = 0; i < N; i++) {
      int S = (*this)[i].size();
      g_dp[i].resize(S, ident);
      g_ndp[i].resize(S, ident);
      memo[i].resize(S);
      seen[i].resize(S, false);
    }
    stack<int> stk;
    stk.push(0);
    vector<int> par(N), ord(N);
    int index = 0;
    par[0] = -1;
    while (!stk.empty()) {
      int node = stk.top();
      stk.pop();
      ord[index++] = node;
      for (auto &e : (*this)[node]) {
        if (e.to == par[node])
          continue;
        stk.push(e.to);
        par[e.to] = node;
      }
    }
    for (int k = ord.size() - 1; k >= 0; k--) {
      int idx = ord[k];
      for (int i = 0; i < (int)((*this)[idx].size()); i++) {
        auto &e = (*this)[idx][i];
        if (e.to == par[idx])
          continue;
        if (!seen[idx][i]) {
          memo[idx][i] = gg(subdp[e.to], e);
          seen[idx][i] = true;
        }
        subdp[idx] = f(subdp[idx], memo[idx][i]);
      }
    }
    vector<sum_t> top(N, ident);
    for (int k = 0; k < (int)(ord.size()); k++) {
      int idx = ord[k];
      sum_t buff{ident};
      for (int i = 0; i < (int)((*this)[idx].size()); i++) {
        auto &e = (*this)[idx][i];
        g_ndp[idx][i] = buff;
        if (!seen[idx][i]) {
          memo[idx][i] = gg(par[idx] == e.to ? top[idx] : subdp[e.to], e);
          seen[idx][i] = true;
        }
        g_dp[idx][i] = memo[idx][i];
        buff = f(buff, g_dp[idx][i]);
      }
      dp[idx] = buff;
      buff = ident;
      for (int i = (*this)[idx].size() - 1; i >= 0; i--) {
        auto &e = (*this)[idx][i];
        if (e.to != par[idx])
          top[e.to] = f(g_ndp[idx][i], buff);
        g_ndp[idx][i] = f(g_ndp[idx][i], buff);
        buff = f(buff, g_dp[idx][i]);
      }
    }
    return make_pair(dp, memo);
  }
  //
  //
  //
  // TODO:lca,centroid,diameter,hl-decomposition
};
// グローバルでの使用のみ想定

int N, M;
Graph<int> G;
Graph<int> H;
vi id[202020];

void solve() {
  cout << G.Gbuild_SCC() << endl;
  H.build(G.SCC_T);

  for (int i = 0; i < N; i++) {
    id[G.SCC(i)].emplace_back(i);
  }

  auto V = H.TopologicalSort();
  for (auto &x : V) {
    cout << id[x].size() << " ";
    for (auto &i : id[x])
      cout << i << " ";
    cout << endl;
  }
}

signed main() {
  cin.tie(nullptr);
  ios::sync_with_stdio(false);

  cin >> N >> M;
  G.resize(N);
  for (int i = 0; i < M; i++) {
    int a, b;
    cin >> a >> b;
    G.add_edge_directed(a, b);
  }

  solve();
  return 0;
}


Fixed submission:
#define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
#define int long long
#define ll long long
using ull = unsigned long long;
using namespace std;
#define dump(x)                                                                \
  if (dbg) {                                                                   \
    cerr << #x << " = " << (x) << endl;                                        \
  }
#define overload4(_1, _2, _3, _4, name, ...) name
#define FOR1(n) for (ll i = 0; i < (n); ++i)
#define FOR2(i, n) for (ll i = 0; i < (n); ++i)
#define FOR3(i, a, b) for (ll i = (a); i < (b); ++i)
#define FOR4(i, a, b, c) for (ll i = (a); i < (b); i += (c))
#define FOR(...) overload4(__VA_ARGS__, FOR4, FOR3, FOR2, FOR1)(__VA_ARGS__)
#define FORR(i, a, b) for (int i = (a); i <= (b); ++i)
#define bit(n, k) (((n) >> (k)) & 1) /*nのk bit目*/
namespace mydef {
const int INF = 1ll << 60;
const int MOD = 1e9 + 7;
template <class T> bool chmin(T &a, const T &b) {
  if (a > b) {
    a = b;
    return 1;
  } else
    return 0;
}
template <class T> bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return 1;
  } else
    return 0;
}
void Yes(bool flag = true) {
  if (flag)
    cout << "Yes" << endl;
  else
    cout << "No" << endl;
}
void No(bool flag = true) { Yes(!flag); }
void YES(bool flag = true) {
  if (flag)
    cout << "YES" << endl;
  else
    cout << "NO" << endl;
}
void NO(bool flag = true) { YES(!flag); }
template <typename A, size_t N, typename T>
void Fill(A (&array)[N], const T &val) {
  std::fill((T *)array, (T *)(array + N), val);
}
bool dbg = true;
} // namespace mydef
using namespace mydef;
#define pb push_back
// #define mp make_pair
#define eb emplace_back
#define lb lower_bound
#define ub upper_bound
#define all(v) (v).begin(), (v).end()
#define SZ(x) ((int)(x).size())
#define vi vector<int>
#define vvi vector<vector<int>>
#define vp vector<pair<int, int>>
#define vvp vector<vector<pair<int, int>>>
#define pi pair<int, int>
// #define P pair<int, int>
// #define V vector<int>
// #define S set<int>
#define asn ans

#include <bits/stdc++.h>
using namespace std;
template <typename T> struct edge {
  int to, id;
  T cost;
  edge(int to) : to(to), id(-1), cost(1) {}
  edge(int to, T cost) : to(to), id(-1), cost(cost) {}
  edge(int to, T cost, int id) : to(to), id(id), cost(cost) {}
  operator int() const { return to; }
  /*
      edge& operator=(const int& x) {
          to = x;
          return *this;
      }
      */
};
template <typename T> class Graph : public vector<vector<edge<T>>> {
  //--------Basic--------
private:
  using Edge = edge<T>;
  int M; // M:辺の数
  bool undirected = false;
  bool directed = false;
  bool unweighted = false;
  bool weighted = false;
  bool tree = false;
  //
public:
  vector<int> A, B;
  vector<T> C;
  //
public:
  Graph() = default;
  // unweighted
  void add_edge_undirected(int u, int v, int id = -1) {
    assert(!directed);
    assert(!weighted);
    undirected = true;
    unweighted = true;
    (*this)[u].emplace_back(Edge{v, 1, id});
    (*this)[v].emplace_back(Edge{u, 1, id});
  }
  void build_undirected(int m) {
    assert(!(*this).empty());
    assert(!directed);
    assert(!weighted);
    undirected = true;
    unweighted = true;
    M = m;
    A.resize(m);
    B.resize(m);
    for (int i = 0; i < m; i++) {
      int u, v;
      cin >> u >> v;
      u--;
      v--;
      A[i] = u;
      B[i] = v;
      (*this)[u].emplace_back(Edge{v, 1, i});
      (*this)[v].emplace_back(Edge{u, 1, i});
    }
  }
  void build_undirected(int n, int m) {
    (*this).resize(n);
    build_undirected(m);
  }
  void add_edge_directed(int u, int v, int id = -1) {
    assert(!undirected);
    assert(!weighted);
    directed = true;
    unweighted = true;
    (*this)[u].emplace_back(Edge{v, 1, id});
  }
  void build_directed(int m) {
    assert(!(*this).empty());
    assert(!undirected);
    assert(!weighted);
    directed = true;
    unweighted = true;
    M = m;
    A.resize(M);
    B.resize(M);
    for (int i = 0; i < m; i++) {
      int u, v;
      cin >> u >> v;
      u--;
      v--;
      A[i] = u;
      B[i] = v;
      (*this)[u].emplace_back(Edge{v, 1, i});
    }
  }
  void build_directed(int n, int m) {
    (*this).resize(n);
    build_directed(m);
  }
  // weighed
  void add_edge_undirected_weighed(int u, int v, T cost, int id = -1) {
    assert(!directed);
    assert(!unweighted);
    undirected = true;
    weighted = true;
    (*this)[u].emplace_back(Edge{v, cost, id});
    (*this)[v].emplace_back(Edge{u, cost, id});
  }
  void build_undirected_weighted(int m) {
    assert(!(*this).empty());
    assert(!directed);
    assert(!unweighted);
    undirected = true;
    weighted = true;
    M = m;
    A.resize(m);
    B.resize(m);
    C.resize(m);
    for (int i = 0; i < m; i++) {
      int u, v;
      T cost;
      cin >> u >> v >> cost;
      u--;
      v--;
      A[i] = u;
      B[i] = v;
      C[i] = cost;
      (*this)[u].emplace_back(Edge{v, cost, i});
      (*this)[v].emplace_back(Edge{u, cost, i});
    }
  }
  void build_undirected_weighted(int n, int m) {
    (*this).resize(n);
    build_undirected_weighted(m);
  }
  void add_edge_directed_weighted(int u, int v, T cost, int id = -1) {
    assert(!undirected);
    assert(!unweighted);
    directed = true;
    weighted = true;
    (*this)[u].emplace_back(Edge{v, cost, id});
  }
  void build_directed_weighted(int m) {
    assert(!(*this).empty());
    assert(!undirected);
    assert(!unweighted);
    directed = true;
    weighted = true;
    M = m;
    A.resize(m);
    B.resize(m);
    C.resize(m);
    for (int i = 0; i < m; i++) {
      int u, v;
      T cost;
      cin >> u >> v >> cost;
      u--;
      v--;
      A[i] = u;
      B[i] = v;
      C[i] = cost;
      (*this)[u].emplace_back(Edge{v, cost, i});
    }
  }
  void build_directed_weighted(int n, int m) {
    (*this).resize(n);
    build_directed_weighted(m);
  }
  void build(vector<vector<int>> G) {
    int N = G.size();
    (*this).resize(N);
    for (int i = 0; i < N; i++) {
      for (auto &x : G[i]) {
        add_edge_directed(i, x);
      }
    }
  }
  // tree
  void istree() { tree = true; }
  void build_tree(int n) {
    istree();
    build_undirected(n, n - 1);
  }
  void build_tree_weighted(int n) {
    istree();
    build_undirected_weighted(n, n - 1);
  }
  // print state
  void state() {
    cerr << endl
         << "Print State" << endl
         << "Edge :" << endl
         << "    Directed   : " << (directed ? "Yes" : "No") << endl
         << "    Undirected : " << (undirected ? "Yes" : "No") << endl
         << "    Weighted   : " << (weighted ? "Yes" : "No") << endl
         << "    Unweighted : " << (unweighted ? "Yes" : "No") << endl
         << "    Tree       : " << (tree ? "Yes" : "No") << endl
         << "Test" << endl
         << "    test_bipartite(verified) : Yes" << endl
         << "Usable Functions : Graph" << endl
         << "    Dijkstra(verified) : Yes" << endl
         << "    Warshall–Floyd(verified) : Yes" << endl
         << "    Kruskal(verified) : Yes" << endl
         << "    TopologicalSort(verified) : Yes" << endl
         << "    StronglyConnectedComponent(unverified): "
         << (StronglyConnectedComponent_ready ? "Yes" : "No") << endl
         << "Usable Funcitons : Tree" << endl
         << "    ReRooting(verified) : Yes" << endl;
  }
  //
  //--------union-find tree--------
  struct UnionFind {
    vector<int> data;
    UnionFind(int n) { data.assign(n, -1); }
    bool unite(int x, int y) {
      x = root(x), y = root(y);
      if (x == y)
        return (false);
      if (data[x] > data[y])
        swap(x, y);
      data[x] += data[y];
      data[y] = x;
      return (true);
    }
    int root(int k) {
      if (data[k] < 0)
        return (k);
      return (data[k] = root(data[k]));
    }
    bool same(int x, int y) { return root(x) == root(y); }
    int size(int k) { return (-data[root(k)]); }
  };
  //
  //--------test_bipartite--------
  // verified
public:
  bool test_bipartite() {
    int N = (*this).size();
    UnionFind uf(N * 2);
    for (int i = 0; i < N; i++) {
      for (auto &e : (*this)[i]) {
        uf.unite(i, e.to + N);
        uf.unite(i + N, e.to);
      }
    }
    for (int i = 0; i < N; i++) {
      if (uf.same(i, i + N))
        return false;
    }
    return true;
  }
  //
  //--------reverse--------
public:
  void reverse() {
    int N = (*this).size();
    vector<pair<int, Edge>> V;
    V.reserve(M);
    for (int i = 0; i < N; i++) {
      for (auto &e : (*this)[i]) {
        V.emplace_back(make_pair(i, e));
      }
    }
    (*this).erase((*this).begin(), (*this).end());
    (*this).resize(N);
    for (auto &p : V)
      (*this)[p.second.to].emplace_back(
          Edge{p.first, p.second.cost, p.second.id});
  }
  //
  //--------Dijkstra--------
  // verified
public:
  // pair<vector<T>, vector<int>> Dijkstra(int s) {
  vector<T> Dijkstra(int s) {
    const T INF = numeric_limits<T>::max() / 5;
    using P = pair<T, int>;
    int N = (*this).size();
    vector<T> dist(N, INF);
    // vector<int> bef(N, -1);
    priority_queue<P, vector<P>, greater<P>> que;
    dist[s] = 0;
    que.emplace(dist[s], s);
    while (!que.empty()) {
      P p = que.top();
      que.pop();
      int now = p.second;
      if (dist[now] < p.first)
        continue;
      for (auto &p : (*this)[now]) {
        int nxt = p.to;
        T cost = p.cost;
        if (dist[nxt] > dist[now] + cost) {
          dist[nxt] = dist[now] + cost;
          // bef[nxt] = now;
          que.emplace(dist[nxt], nxt);
        }
      }
    }
    return dist;
    // return make_pair(dist, bef);
  }
  //
  //
  //--------Warshall–Floyd--------
  // verified
public:
  vector<vector<T>> WarshallFloyd() {
    int N = (*this).size();
    const T INF = numeric_limits<T>::max() / 3;
    vector<vector<T>> ret(N, vector<T>(N, INF));
    for (int i = 0; i < N; i++) {
      for (auto &e : (*this)[i]) {
        ret[i][e.to] = e.cost;
      }
      ret[i][i] = 0;
    }
    for (int k = 0; k < N; k++) {
      for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
          if (ret[i][j] > ret[i][k] + ret[k][j])
            ret[i][j] = ret[i][k] + ret[k][j];
        }
      }
    }
    return ret;
  }
  //
  //
  //--------Kruskal--------
  // verified
private:
  vector<int> Kruskal_data;
  bool Kruskal_unite(int x, int y) {
    x = Kruskal_root(x), y = Kruskal_root(y);
    if (x == y)
      return (false);
    if (Kruskal_data[x] > Kruskal_data[y])
      swap(x, y);
    Kruskal_data[x] += Kruskal_data[y];
    Kruskal_data[y] = x;
    return (true);
  }
  int Kruskal_root(int k) {
    if (Kruskal_data[k] < 0)
      return (k);
    return (Kruskal_data[k] = Kruskal_root(Kruskal_data[k]));
  }
  bool Kruskal_same(int x, int y) { return Kruskal_root(x) == Kruskal_root(y); }
  int Kruskal_size(int k) { return (-Kruskal_data[Kruskal_root(k)]); }
  //
public:
  template <class Compare = less<T>>
  pair<T, vector<bool>> Kruskal(bool flag = false) {
    struct edge2 {
      int from, to;
      T cost;
      bool used;
      int id;
      edge2(int from, int to, T cost, int id)
          : from(from), to(to), cost(cost), used(false), id(id) {}
    };
    vector<edge2> edges;
    int N = (*this).size();
    Kruskal_data.assign(N, -1);
    for (int i = 0; i < (int)(*this).size(); i++) {
      auto &V = (*this)[i];
      for (auto &e : V) {
        edges.emplace_back(i, e.to, e.cost, e.id);
      }
    }
    sort(edges.begin(), edges.end(), [](const edge2 &a, const edge2 &b) {
      return Compare()(a.cost, b.cost);
    });
    T ret = 0;
    vector<bool> V;
    if (flag)
      V.resize(M, false);
    for (auto &e : edges) {
      if (Kruskal_unite(e.from, e.to)) {
        ret += e.cost;
        e.used = true;
      }
    }
    if (flag)
      for (auto &e : edges) {
        assert(e.id >= 0 && e.id < M);
        if (e.used)
          V[e.id] = true;
      }
    if (N == Kruskal_size(0))
      return make_pair(ret, V);
    else
      return make_pair(-1, V);
  }
  //
  //--------StronglyConnectedComponent--------
  // Unverified
public:
  vector<vector<int>> SCC_R, SCC_T;
  //
private:
  bool StronglyConnectedComponent_ready = false;
  vector<int> SCC_cmp, SCC_ord;
  vector<bool> SCC_used;
  //
public:
  int Gbuild_SCC() {
    cerr << "Please Verify" << endl;
    assert(!undirected);
    assert(directed);
    int N = (*this).size();
    SCC_R.resize(N);
    for (int i = 0; i < N; i++) {
      for (auto &x : (*this)[i]) {
        SCC_R[x].emplace_back(i);
      }
    }
    SCC_cmp.resize(N);
    fill(SCC_cmp.begin(), SCC_cmp.end(), -1);
    SCC_used.resize(N);
    fill(SCC_used.begin(), SCC_used.end(), false);
    StronglyConnectedComponent_ready = true;
    return build_StronglyConnectedComponent_init();
  }
  int SCC(int k) {
    assert(StronglyConnectedComponent_ready);
    return SCC_cmp[k];
  }
  //
private:
  void build_StronglyConnectedComponent_dfs(int now) {
    if (SCC_used[now])
      return;
    SCC_used[now] = true;
    for (auto nxt : (*this)[now])
      build_StronglyConnectedComponent_dfs(nxt);
    SCC_ord.emplace_back(now);
  }
  void build_StronglyConnectedComponent_rdfs(int now, int count) {
    if (SCC_cmp[now] != -1)
      return;
    SCC_cmp[now] = count;
    for (auto to : SCC_R[now])
      build_StronglyConnectedComponent_rdfs(to, count);
  }
  int build_StronglyConnectedComponent_init() {
    int n = (int)(*this).size();
    for (int i = 0; i < n; i++)
      build_StronglyConnectedComponent_dfs(i);
    std::reverse(SCC_ord.begin(), SCC_ord.end());
    int group = 0;
    for (auto &i : SCC_ord) {
      if (SCC_cmp[i] == -1) {
        build_StronglyConnectedComponent_rdfs(i, group);
        group++;
      }
    }
    SCC_T.resize(group);
    for (int i = 0; i < n; i++) {
      for (auto &to : (*this)[i]) {
        int s = SCC_cmp[i], t = SCC_cmp[to];
        if (s != t)
          SCC_T[s].emplace_back(t);
      }
    }
    return group;
  }
  //
  //
  //
  //--------TopologicalSort--------
  // verified
public:
  vector<int> TopologicalSort() {
    assert(!undirected);
    int N = (*this).size();
    vector<int> deg(N);
    for (auto &V : (*this))
      for (auto &e : V)
        deg[e.to]++;
    stack<int> st;
    for (int i = 0; i < N; i++)
      if (deg[i] == 0)
        st.emplace(i);
    vector<int> ret;
    ret.reserve(N);
    while (!st.empty()) {
      auto x = st.top();
      st.pop();
      ret.emplace_back(x);
      for (auto &e : (*this)[x])
        if (--deg[e.to] == 0)
          st.emplace(e.to);
    }
    return ret;
  }
  //
  //
  //--------Tree--------
  //
  //
  //--------ReRooting--------
public:
  template <typename sum_t>
  pair<vector<sum_t>, vector<vector<sum_t>>>
  Tbuild_ReRooting(const function<sum_t(sum_t, sum_t)> f,
                   const function<sum_t(sum_t, Edge)> gg, sum_t ident) {
    assert(tree);
    int N = (*this).size();
    vector<sum_t> subdp(N, ident), dp(N, ident);
    vector<vector<sum_t>> g_dp(N), g_ndp(N), memo(N);
    vector<vector<bool>> seen(N);
    for (int i = 0; i < N; i++) {
      int S = (*this)[i].size();
      g_dp[i].resize(S, ident);
      g_ndp[i].resize(S, ident);
      memo[i].resize(S);
      seen[i].resize(S, false);
    }
    stack<int> stk;
    stk.push(0);
    vector<int> par(N), ord(N);
    int index = 0;
    par[0] = -1;
    while (!stk.empty()) {
      int node = stk.top();
      stk.pop();
      ord[index++] = node;
      for (auto &e : (*this)[node]) {
        if (e.to == par[node])
          continue;
        stk.push(e.to);
        par[e.to] = node;
      }
    }
    for (int k = ord.size() - 1; k >= 0; k--) {
      int idx = ord[k];
      for (int i = 0; i < (int)((*this)[idx].size()); i++) {
        auto &e = (*this)[idx][i];
        if (e.to == par[idx])
          continue;
        if (!seen[idx][i]) {
          memo[idx][i] = gg(subdp[e.to], e);
          seen[idx][i] = true;
        }
        subdp[idx] = f(subdp[idx], memo[idx][i]);
      }
    }
    vector<sum_t> top(N, ident);
    for (int k = 0; k < (int)(ord.size()); k++) {
      int idx = ord[k];
      sum_t buff{ident};
      for (int i = 0; i < (int)((*this)[idx].size()); i++) {
        auto &e = (*this)[idx][i];
        g_ndp[idx][i] = buff;
        if (!seen[idx][i]) {
          memo[idx][i] = gg(par[idx] == e.to ? top[idx] : subdp[e.to], e);
          seen[idx][i] = true;
        }
        g_dp[idx][i] = memo[idx][i];
        buff = f(buff, g_dp[idx][i]);
      }
      dp[idx] = buff;
      buff = ident;
      for (int i = (*this)[idx].size() - 1; i >= 0; i--) {
        auto &e = (*this)[idx][i];
        if (e.to != par[idx])
          top[e.to] = f(g_ndp[idx][i], buff);
        g_ndp[idx][i] = f(g_ndp[idx][i], buff);
        buff = f(buff, g_dp[idx][i]);
      }
    }
    return make_pair(dp, memo);
  }
  //
  //
  //
  // TODO:lca,centroid,diameter,hl-decomposition
};
// グローバルでの使用のみ想定

int N, M;
Graph<int> G;
Graph<int> H;
vi id[502020];

void solve() {
  cout << G.Gbuild_SCC() << endl;
  H.build(G.SCC_T);

  for (int i = 0; i < N; i++) {
    id[G.SCC(i)].emplace_back(i);
  }

  auto V = H.TopologicalSort();
  for (auto &x : V) {
    cout << id[x].size() << " ";
    for (auto &i : id[x])
      cout << i << " ";
    cout << endl;
  }
}

signed main() {
  cin.tie(nullptr);
  ios::sync_with_stdio(false);

  cin >> N >> M;
  G.resize(N);
  for (int i = 0; i < M; i++) {
    int a, b;
    cin >> a >> b;
    G.add_edge_directed(a, b);
  }

  solve();
  return 0;
}