#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> ii;
const int INF = 1e9;
const ll LINF = 1e18;
const int N = 1e5 + 5;
int n, m;
int p[N]; // p[u]: cha của u
int sz[N]; // sz[u]: Số đỉnh có trong tập hợp (cây / thành phần liên thông) do u đại diện
void initSet() {
for (int u = 1; u <= n; u++) {
p[u] = u;
sz[u] = 1;
}
}
// Tìm đại diện của tập hợp (cây / thành phần liên thông) chứa u
int findSet(int u) {
if (p[u] == u) return u;
return p[u] = findSet(p[u]);
}
// Gộp tập hợp chứa u với tập hợp chứa v
// Trả về true nếu gộp thành công, false nếu u và v đã chung một tập hợp
bool unionSet(int u, int v) {
u = findSet(u);
v = findSet(v);
if (u == v) return false;
if (sz[u] < sz[v]) swap(u, v);
p[v] = u;
sz[u] += sz[v];
return true;
}
vector<int> comp[N]; // comp[u]: danh sách các đỉnh có thành phần liên thông do u đại diện
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
initSet();
int num_cc = n; // Số thành phần liên thông
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
num_cc -= unionSet(u, v);
}
for (int u = 1; u <= n; u++) {
comp[findSet(u)].push_back(u);
}
cout << num_cc << '\n';
for (int u = 1; u <= n; u++) {
if (findSet(u) == u) {
cout << comp[u].size() << ' ';
for (int v : comp[u]) cout << v << ' ';
cout << '\n';
}
}
}
I2luY2x1ZGUgPGJpdHMvc3RkYysrLmg+IAoKdXNpbmcgbmFtZXNwYWNlIHN0ZDsgIAoKdHlwZWRlZiBsb25nIGxvbmcgbGw7ICAKdHlwZWRlZiBwYWlyPGludCwgaW50PiBpaTsgIAoKY29uc3QgaW50IElORiA9IDFlOTsgIApjb25zdCBsbCBMSU5GID0gMWUxODsgIAoKY29uc3QgaW50IE4gPSAxZTUgKyA1OyAKCmludCBuLCBtOyAKaW50IHBbTl07IC8vIHBbdV06IGNoYSBj4bunYSB1ICAKaW50IHN6W05dOyAvLyBzelt1XTogU+G7kSDEkeG7iW5oIGPDsyB0cm9uZyB04bqtcCBo4bujcCAoY8OieSAvIHRow6BuaCBwaOG6p24gbGnDqm4gdGjDtG5nKSBkbyB1IMSR4bqhaSBkaeG7h24gCgp2b2lkIGluaXRTZXQoKSB7Cglmb3IgKGludCB1ID0gMTsgdSA8PSBuOyB1KyspIHsKCQlwW3VdID0gdTsgCgkJc3pbdV0gPSAxOyAgCgl9Cn0KCi8vIFTDrG0gxJHhuqFpIGRp4buHbiBj4bunYSB04bqtcCBo4bujcCAoY8OieSAvIHRow6BuaCBwaOG6p24gbGnDqm4gdGjDtG5nKSBjaOG7qWEgdSAKaW50IGZpbmRTZXQoaW50IHUpIHsKCWlmIChwW3VdID09IHUpIHJldHVybiB1OyAgCglyZXR1cm4gcFt1XSA9IGZpbmRTZXQocFt1XSk7IAp9CgovLyBH4buZcCB04bqtcCBo4bujcCBjaOG7qWEgdSB24bubaSB04bqtcCBo4bujcCBjaOG7qWEgdgovLyBUcuG6oyB24buBIHRydWUgbuG6v3UgZ+G7mXAgdGjDoG5oIGPDtG5nLCBmYWxzZSBu4bq/dSB1IHbDoCB2IMSRw6MgY2h1bmcgbeG7mXQgdOG6rXAgaOG7o3AKYm9vbCB1bmlvblNldChpbnQgdSwgaW50IHYpIHsKCXUgPSBmaW5kU2V0KHUpOyAKCXYgPSBmaW5kU2V0KHYpOyAKCWlmICh1ID09IHYpIHJldHVybiBmYWxzZTsgIAoJaWYgKHN6W3VdIDwgc3pbdl0pIHN3YXAodSwgdik7IAoJcFt2XSA9IHU7ICAKCXN6W3VdICs9IHN6W3ZdOyAKCXJldHVybiB0cnVlOyAKfQoKdmVjdG9yPGludD4gY29tcFtOXTsgLy8gY29tcFt1XTogZGFuaCBzw6FjaCBjw6FjIMSR4buJbmggY8OzIHRow6BuaCBwaOG6p24gbGnDqm4gdGjDtG5nIGRvIHUgxJHhuqFpIGRp4buHbgoKaW50IG1haW4oKSB7Cglpb3M6OnN5bmNfd2l0aF9zdGRpbyhmYWxzZSk7IAoJY2luLnRpZShudWxscHRyKTsgCQoJY2luID4+IG4gPj4gbTsgCgoJaW5pdFNldCgpOyAgCgoJaW50IG51bV9jYyA9IG47IC8vIFPhu5EgdGjDoG5oIHBo4bqnbiBsacOqbiB0aMO0bmcKCWZvciAoaW50IGkgPSAwOyBpIDwgbTsgaSsrKSB7CgkJaW50IHUsIHY7IAoJCWNpbiA+PiB1ID4+IHY7ICAKCQludW1fY2MgLT0gdW5pb25TZXQodSwgdik7IAoJfQoKCWZvciAoaW50IHUgPSAxOyB1IDw9IG47IHUrKykgewoJCWNvbXBbZmluZFNldCh1KV0ucHVzaF9iYWNrKHUpOyAKCX0KCgljb3V0IDw8IG51bV9jYyA8PCAnXG4nOyAKCQoJZm9yIChpbnQgdSA9IDE7IHUgPD0gbjsgdSsrKSB7CgkJaWYgKGZpbmRTZXQodSkgPT0gdSkgeyAgCgkJCWNvdXQgPDwgY29tcFt1XS5zaXplKCkgPDwgJyAnOyAgCgkJCWZvciAoaW50IHYgOiBjb21wW3VdKSBjb3V0IDw8IHYgPDwgJyAnOyAgIAoJCQljb3V0IDw8ICdcbic7IAoJCX0KCX0KfQ==