You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

80 lines
2.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 300010;
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
// 点双连通分量
int dfn[N], low[N], stk[N], ts, top, root;
vector<int> bcc[N];
int bcnt;
void tarjan(int u, int fa) {
int cnt = 0; // 去掉u之后还剩多少个连通块
low[u] = dfn[u] = ++ts;
stk[++top] = u;
int son = 0;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == fa) continue;
if (!dfn[v]) {
son++;
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
cnt++; // u是割点把u删除掉所有的v就都孤立了多出来1个孤立块v
int x;
bcnt++;
do {
x = stk[top--];
bcc[bcnt].push_back(x);
} while (x != v); // 将子树出栈
bcc[bcnt].push_back(u); // 把割点/树根也丢到点双里
}
}
low[u] = min(low[u], dfn[v]);
}
if (fa == -1 && son == 0) bcc[++bcnt].push_back(u); // 特判独立点,单独成点双,本题可以不用管这个
if (u != root) cnt++; // u不是根节点的话除了自己的子树还有他的父节点也断开了连接
ans = max(ans, cnt); // 不断更新生成的孤立块最大数量
}
int ans; // 在处理每个连通块时,尝试删除每一个割点,记录可以生成的孤立块的数量最大值
int main() {
int n, m;
while (scanf("%d %d", &n, &m), n || m) {
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(stk, 0, sizeof stk);
memset(h, -1, sizeof h);
idx = ts = top = 0;
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
a++, b++; // 本题点号从0开始+1后平移到1开始 0≤a,b<n
add(a, b), add(b, a);
}
// 连通块的最大(多)数量
ans = 0;
int cnt = 0; // 原图中块数量(互相孤立的有多少个块)
for (root = 1; root <= n; root++)
if (!dfn[root]) {
tarjan(root, -1);
cnt++; // 记录有多少个独立的块
}
printf("%d\n", cnt + ans - 1);
}
return 0;
}