Solution 1:
We can reason as follows.
We can repeatedly identify the minimum degree vertex v of the graph, update the answer to be at least s, and then remove v in O(M+N) time. However, computing connected components after every vertex removal naively takes O(NM) time. We can speed this by reversing the sequence of vertex removals (so that we want to maintain connected components after adding instead of removing a vertex), and then using a Disjoint Set Union data structure. The time complexity is O(Mα(N)) (or O(MlogM) if a set is used to identity and remove the minimum degree vertex).
Timothy Qian's code:
#include <bits/stdc++.h> using namespace std; struct DSU { vector<int> e; DSU(int n) { e = vector<int>(n, -1); } int get(int x) { return e[x] < 0 ? x : e[x] = get(e[x]); } bool same_set(int a, int b) { return get(a) == get(b); } int size(int x) { return -e[get(x)]; } bool unite(int x, int y) { x = get(x), y = get(y); if (x == y) return false; if (e[x] > e[y]) swap(x, y); e[x] += e[y]; e[y] = x; return true; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<vector<int>> g(n); vector<int> deg(n); for (int i = 0; i < m; ++i) { int u, v; cin >> u >> v; --u, --v; g[u].push_back(v); g[v].push_back(u); ++deg[u]; ++deg[v]; } set<array<int, 2>> vertices; for (int i = 0; i < n; ++i) { vertices.insert({deg[i], i}); } vector<int> order; vector<int> degrees; vector<bool> active(n, true); auto remove = [&]() { auto top = *vertices.begin(); int u = top[1]; int degree = top[0]; order.push_back(u); degrees.push_back(degree); active[u] = false; for (int v : g[u]) { if (active[v]) { vertices.erase({deg[v], v}); --deg[v]; vertices.insert({deg[v], v}); } } vertices.erase({deg[u], u}); }; for (int i = 0; i < n; ++i) { remove(); } reverse(order.begin(), order.end()); reverse(degrees.begin(), degrees.end()); active.assign(n, false); DSU dsu(n); int mx = 1; long long ans = 0; for (int i = 0; i < n; ++i) { int u = order[i]; active[u] = true; for (int v : g[u]) { if (active[v]) { dsu.unite(u, v); mx = max(mx, dsu.size(u)); } } ans = max(ans, 1ll * mx * degrees[i]); } cout << ans << '\n'; return 0; }
Solution 2:
Suppose we are looking for the strongest friendship group where the cow with the minimum number of friends has exactly d friends. We can find such a friendship group as follows: first, repeatedly remove any vertex with degree less than d from the graph, and then return the largest connected component. We can do this in O(M) time for each of d=1,2,…, and so on until the graph is empty. As a friendship group where every member has at least d friends must contain at least (d+1)d2 pairs of friendships, so once (d+1)d2>M, the graph must be empty. Thus, this solution runs in O(M√M) time.
The code solution uses DSU (which adds an extra factor of α(N)), though this may be substituted with any other method of finding connected components (such as BFS or DFS).
Nick Wu's code:
#include <algorithm> #include <cstdio> #include <numeric> #include <vector> using namespace std; struct disjoint_set { vector<int> p, sz; disjoint_set(int n) { p.assign(n, -1); sz.assign(n, 1); } int find(int x) { return p[x] < 0 ? x : (p[x] = find(p[x])); } int getsz(int x) { return sz[find(x)]; } bool merge(int x, int y) { x = find(x); y = find(y); if(x == y) return false; p[x] = y; sz[y] += sz[x]; return true; } }; int main() { int n, m; scanf("%d%d", &n, &m); vector<vector<int>> edges(n); vector<int> edeg(n); for(int i = 0; i < m; i++) { int a, b; scanf("%d%d", &a, &b); a--; b--; edeg[a]++; edeg[b]++; edges[a].push_back(b); edges[b].push_back(a); } int ret = 0; vector<bool> deleted(n); vector<int> active(n); iota(active.begin(), active.end(), 0); for(int mindeg = 1; mindeg * mindeg <= m; mindeg++) { disjoint_set dsu(n); for(int i: active) { for(auto j: edges[i]) { if(!deleted[j] && dsu.merge(i, j)) ret = max(ret, dsu.getsz(i) * mindeg); } } vector<int> nactive; vector<int> q; for(int i: active) { if(edeg[i] == mindeg) { q.push_back(i); } } while(q.size()) { int i = q.back(); q.pop_back(); if(deleted[i]) continue; deleted[i] = true; for(int j: edges[i]) { if(--edeg[j] <= mindeg) { q.push_back(j); } } edges[i].clear(); } for(int i: active) if(edeg[i] > mindeg) nactive.push_back(i); active.swap(nactive); } printf("%d\n", ret); }