点双联通分量(v-DCC, 无向图的极大点双连通子图):
①当第一个节点第一次被访问时,把该节点入栈.
②当dfn[x] <= low[y]成立时(1)从栈顶不断弹出节点,直至节点y被弹出(2)刚才弹出的所有节点与节点x一起构成一个v-DCC.
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f = ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
void print(int x) {
if (x < 0) x = -x, putchar('-');
if (x < 10) putchar(x + '0');
else {
print(x / 10);
putchar(x % 10 + '0');
}
}
const int N = 5e4 + 5, M = 3e5 + 5;
int n, m, rt, head[N], nex[M << 1], ver[M << 1], tot;
int dfn[N], low[N], cut[N], num;
int stk[N], top;
vector<int> dcc[N]; int cnt;
void Addedge(int x, int y) { ver[++tot] = y; nex[tot] = head[x]; head[x] = tot; }
void tarjan(int x) {
dfn[x] = low[x] = ++num;
stk[++top] = x;
if (x == rt && !head[x]) {
// dcc[++cnt].push_back(x);
return;
}
int flag = 0;
for (int i = head[x]; i; i = nex[i]) {
int y = ver[i];
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
if (low[y] >= dfn[x]) {
++flag;
if (x != rt || flag > 1) cut[x] = true;
++cnt; int z;
do {
z = stk[top--];
dcc[cnt].push_back(z);
} while (z != y);
dcc[cnt].push_back(x);
}
} else low[x] = min(low[x], dfn[y]);
}
}
bool cmp(vector<int> a, vector<int> b) {
int mi = min(a.size(), b.size());
for (int i = 0; i < mi; ++i) {
if (a[i] != b[i]) return a[i] < b[i];
}
return a.size() < b.size();
}
int main() {
n = read(); m = read();
while (m--) {
int uu = read(), vv = read();
Addedge(uu, vv); Addedge(vv, uu);
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) {
rt = i; tarjan(i);
}
}
print(cnt); putchar('\n');
for (int i = 1; i <= cnt; ++i) sort(dcc[i].begin(), dcc[i].end());
sort(dcc + 1, dcc + cnt + 1, cmp);
for (int i = 1; i <= cnt; ++i) {
for (vector<int>::iterator it = dcc[i].begin(); it != dcc[i].end(); ++it) {
print(*it); putchar(' ');
}
putchar('\n');
}
return 0;
}