Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Treat every current vertex as a set of original vertices. Merging two current vertices just unions their sets.
By induction, two current sets and are adjacent exactly when every original vertex in is adjacent to every original vertex in .
So a set becomes a universal current vertex iff every vertex in is adjacent to every original vertex outside . Edges inside do not matter.
Now flip to the complement graph. The condition becomes: there is no complement edge between and . That means must be a union of connected components of the complement graph.
If the complement component sizes are , then, unless an original universal vertex already exists, the minimum is and the maximum is . The implementation job is finding complement components without building the dense complement.
Let every current vertex represent the set of original vertices that were merged into it.
The key invariant is:
Two current vertices/sets and are adjacent iff every original vertex in is adjacent to every original vertex in in the original graph.
For singletons this is obvious. If we merge and into , then is adjacent to another current set iff both and were adjacent to . By the invariant, that means all original edges between and exist, and all original edges between and exist. So all original edges between and exist. Induction done.
Now ask when a current set is universal. It must be adjacent to every other current set, so:
Notice the slightly sneaky part: edges inside do not matter. Once those vertices are merged, their internal missing edges are irrelevant. No need to overthink that bit.
Use the complement graph
In the complement graph, an edge means “this original edge is missing.”
The condition above becomes:
That is exactly the definition of being a union of connected components of the complement graph.
So the whole graph operation reduces to this:
Initial universal vertex
If some original vertex has degree , it is already connected to all other vertices. The statement says no operations can be performed, so the answer is:
This also handles .
From now on, assume no such vertex exists. Equivalently, the complement graph has no connected component of size .
Minimum operations
To create a universal current vertex, we need to merge all vertices of some nonempty union of complement components.
If the chosen union has size , it takes exactly merge operations to turn it into one block.
The smallest possible valid set is the smallest complement connected component. Therefore:
where are the sizes of complement connected components.
Maximum operations
Every merge reduces the number of current vertices by , so the absolute upper bound is:
Can we always reach it? Yes, as long as there was no universal vertex initially.
Since every complement component has size at least , pick any complement component and choose two different vertices from it, call them and .
Now imagine an ordering of all vertices where:
Merge vertices one by one into a growing prefix block according to this order.
Before the final operation, the growing block contains but not . So it contains only part of that complement component. Therefore it is not a union of complement components, so it is not universal.
All other unmerged vertices are singletons, and no singleton is universal because we assumed no initial universal vertex.
Thus no universal vertex appears until the very last merge, when only one vertex remains. A one-vertex graph is universal vacuously. So:
That is the whole answer once we know the complement component sizes.
Computing complement connected components efficiently
The complement graph can be dense, so building it is a non-starter.
Store the original graph. During BFS in the complement graph, from a vertex we need every still-unvisited vertex such that is not an original edge.
Maintain a set unvis of unvisited vertices. For each popped vertex $v, scan through unvis`:
u is not adjacent to v in the original graph, then it is reachable by a complement edge, so remove it from unvis and push it;Why is this fast enough? Each removed vertex disappears forever. Each skipped pair corresponds to an actual original edge. Since , the total number of useful checks is fine. The set gives clean erase-while-iterating behavior.
Algorithm:
0 0.unvis BFS trick.best be the smallest component size.best - 1 and n - 1.#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n + 1);
vector<int> deg(n + 1, 0);
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
adj[x].push_back(y);
adj[y].push_back(x);
deg[x]++;
deg[y]++;
}
for (int i = 1; i <= n; i++) {
if (deg[i] == n - 1) {
cout << "0 0\n";
return 0;
}
sort(adj[i].begin(), adj[i].end());
}
set<int> unvis;
for (int i = 1; i <= n; i++) unvis.insert(i);
int best = n;
queue<int> q;
while (!unvis.empty()) {
int start = *unvis.begin();
unvis.erase(start);
q.push(start);
int sz = 0;
while (!q.empty()) {
int v = q.front();
q.pop();
sz++;
for (auto it = unvis.begin(); it != unvis.end(); ) {
int u = *it;
if (!binary_search(adj[v].begin(), adj[v].end(), u)) {
it = unvis.erase(it);
q.push(u);
} else {
++it;
}
}
}
best = min(best, sz);
}
cout << best - 1 << ' ' << n - 1 << '\n';
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n + 1);
vector<int> deg(n + 1, 0);
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
adj[x].push_back(y);
adj[y].push_back(x);
deg[x]++;
deg[y]++;
}
for (int i = 1; i <= n; i++) {
if (deg[i] == n - 1) {
cout << "0 0\n";
return 0;
}
sort(adj[i].begin(), adj[i].end());
}
set<int> unvis;
for (int i = 1; i <= n; i++) unvis.insert(i);
int best = n;
queue<int> q;
while (!unvis.empty()) {
int start = *unvis.begin();
unvis.erase(start);
q.push(start);
int sz = 0;
while (!q.empty()) {
int v = q.front();
q.pop();
sz++;
for (auto it = unvis.begin(); it != unvis.end(); ) {
int u = *it;
if (!binary_search(adj[v].begin(), adj[v].end(), u)) {
it = unvis.erase(it);
q.push(u);
} else {
++it;
}
}
}
best = min(best, sz);
}
cout << best - 1 << ' ' << n - 1 << '\n';
return 0;
}