#include #include #include #include #include #include #include #include typedef long long llong; const int MAXN = 2000 + 10; const int MAXM = 100000 + 10; const int INF = 1e9; int n, m; int answer; int t[MAXN]; int low[MAXN]; bool vis[MAXN]; bool inTree[MAXM]; std::vector tree; std::vector > edges; std::vector > g[MAXN]; std::set > all; std::set bridges; int timer; void findTree(int node) { vis[node] = true; for (const auto &[u, idx] : g[node]) { if (vis[u]) { continue; } inTree[idx] = true; tree.push_back(idx); findTree(u); } } void findInitialBridges(int node, int par) { vis[node] = true; t[node] = low[node] = ++timer; int cntIn = 0; for (const auto &[u, idx] : g[node]) { if (u == par) { cntIn++; continue; } if (vis[u]) { low[node] = std::min(low[node], t[u]); continue; } findInitialBridges(u, node); low[node] = std::min(low[node], low[u]); if (low[u] > t[node]) { bridges.insert(idx); } } if (cntIn > 1) { low[node] = std::min(low[node], t[par]); } } void findBridges(int node, int par, int banned) { vis[node] = true; t[node] = low[node] = ++timer; int cntIn = 0; for (const auto &[u, idx] : g[node]) { if (u == par || banned == idx) { if (u == par && banned != idx) cntIn++; continue; } if (vis[u]) { low[node] = std::min(low[node], t[u]); continue; } findBridges(u, node, banned); low[node] = std::min(low[node], low[u]); if (low[u] > t[node]) { if (bridges.count(idx)) continue; all.insert({std::min(banned, idx), std::max(banned, idx)}); } } if (cntIn > 1) { low[node] = std::min(low[node], t[par]); } } std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); void solve() { findTree(1); std::vector > sure; std::vector > edges2; for (int i = 0 ; i < m ; ++i) { auto [u, v] = edges[i]; if (inTree[i + 1]) { sure.push_back({u, v}); } else { edges2.push_back({u, v}); } } edges = edges2; std::shuffle(edges.begin(), edges.end(), rng); while (edges.size() > 4e4) edges.pop_back(); for (const auto &v : sure) { edges.push_back(v); } for (int i = 1 ; i <= n ; ++i) { g[i].clear(); } for (int i = 0 ; i < edges.size() ; ++i) { auto [u, v] = edges[i]; g[u].push_back({v, i + 1}); g[v].push_back({u, i + 1}); } tree.clear(); std::fill(vis + 1, vis + 1 + n, 0); findTree(1); std::fill(vis + 1, vis + 1 + n, 0); findInitialBridges(1, 0); for (const int &banned : tree) { if (bridges.count(banned)) { continue; } std::fill(vis + 1, vis + 1 + n, 0); std::fill(low + 1, low + 1 + n, 0); std::fill(t + 1, t + 1 + n, 0); timer = 0; findBridges(1, 0, banned); } int sz = bridges.size(); std::cout << (m - 1) * sz - (sz * (sz - 1)) / 2 + all.size() << '\n'; } void input() { std::cin >> n >> m; for (int i = 1 ; i <= m ; ++i) { int u, v; std::cin >> u >> v; g[u].push_back({v, i}); g[v].push_back({u, i}); edges.push_back({u, v}); } } void fastIOI() { freopen("ants.in", "r", stdin); freopen("ants.out", "w", stdout); std::ios_base :: sync_with_stdio(0); std::cout.tie(nullptr); std::cin.tie(nullptr); } int main() { fastIOI(); input(); solve(); return 0; }