#include <bits/stdc++.h> using namespace std; const int N = 5005; const int M = 100010; struct Edge { int to, next; } edge[M]; int head[N]; int cntE; void addedge(int u, int v) { edge[cntE].to = v; edge[cntE].next = head[u]; head[u] = cntE++; edge[cntE].to = u; edge[cntE].next = head[v]; head[v] = cntE++; } int dfn[N], low[N], idx; int stk[N], top; bool cut[N]; int block[N]; // 一个割点可能在很多个连通分量 // 如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈 // 那么这个双连通分量的其他顶点也在某个奇圈中 // 如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。 int no; // block[i] 是去掉i这个节点能够多几个联通块 void tarjan(int u, int fa) { dfn[u] = low[u] = ++idx; stk[top++] = u; block[u] = 0; int son = 0; for (int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if (v == fa || v == no) continue; if (!dfn[v]) { son++; tarjan(v, u); low[u] = min(low[u], low[v]); if (u != fa && low[v] >= dfn[u]) { cut[u] = true; block[u]++; } /* 求每一个连通分量 if (low[v] >= dfn[u]) { int x; do { x = stk[--top]; push(x); } while (x != v); push(u); } */ } else { low[u] = min(low[u], dfn[v]); } } if (u == fa) { if (son > 1) cut[u] = 1; block[u] = son-1; } } void init() { memset(dfn, 0, sizeof dfn); top = idx = cntE = 0; } int main() { int n, m; while (~scanf("%d%d", &n, &m)) { int u, v; memset(head, -1, sizeof head); for (int i = 0; i < m; ++i) { scanf("%d%d", &u, &v); addedge(u, v); } int ans = 0; for (u = 0; u < n; ++u) { init(); int cnt = 0; no = u; for (v = 0; v < n; ++v) { if (v != u && !dfn[v]) { tarjan(v, v); cnt++; } } for (v = 0; v < n; ++v) { if (v != u) ans = max(ans, cnt + block[v]); } } printf("%d ", ans); } return 0; }