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.
First stop guessing and characterize a single number. For a fixed value , what values can become as varies? The whole problem is dead without this.
Suppose you want to become some smaller value . If , then is a positive multiple of , and also . That already gives a very sharp inequality between and . Try proving: Also, itself is always reachable by choosing .
Now fix a candidate answer . To have , you need to realize every value using distinct array elements. So this becomes a matching problem: an element can cover value iff
Here’s the exchange trick that makes the check sane instead of disgusting: if some value already exists in the array, you can always reserve one copy of to produce itself. Why? If some larger element was used to make , and a copy of was used to make some smaller , then swapping them still works because can make only very small values, while that larger element can make those too.
So for checking whether mex is possible:
Take one number . We choose any integer , and the value becomes .
What results are possible?
That gives a necessary condition.
Is it sufficient? Yep. If , choose Then , so the remainder is valid, and
So the reachable values from are exactly
That’s the whole damn problem in one line.
To make , we need every value to appear after the operation.
Each array element can be used once, so this is a matching problem.
An element with value can produce a target value iff
So for a fixed , the question becomes:
Can we assign distinct array elements to all targets under that rule?
This feasibility is monotone: if we can make mex , then we can obviously make any smaller mex too. So binary search is fair game.
This is the key exchange argument that saves us from writing cursed nonsense.
Suppose some value appears in the array. Then there exists an optimal construction where one copy of is left as .
Why is that always safe?
Assume in some solution a copy of is used to make some smaller value , and some other element is used to make .
Since is produced by and , we must have
Since the copy of makes , we know So meaning can also make .
Therefore we can swap roles:
Nothing breaks.
So for a fixed :
Now the remaining copies are just a pool of leftovers. A missing value can be made by a leftover value iff
After reserving one copy of each present value in :
We need to match each with a distinct leftover such that
The greedy strategy is standard and correct:
Why? Because larger missing values only have stricter requirements. Burning a too-large leftover early is just self-sabotage. Classic greedy, classic not-being-an-idiot.
If at some step no leftover satisfies the threshold, then mex is impossible.
Sort the array once.
For a given , scan the sorted array by groups of equal values. During this scan you can build two sorted lists directly:
missing: all values in that do not appear;leftover: all array elements except one reserved copy of each present value in .Both lists are naturally sorted. Then run a two-pointer greedy match.
So:
Total per test case:
Given this flies comfortably.
We proved:
Hence the check is correct, and binary search over returns the maximum feasible mex, which is exactly .
For each test case:
So the total is Memory usage is
Nice, clean, no black magic, no overengineered garbage.
#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 T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n);
for (int &x : a) cin >> x;
sort(a.begin(), a.end());
auto can = [&](int m) -> bool {
vector<int> missing;
vector<int> leftover;
missing.reserve(m);
leftover.reserve(n);
int need = 0; // next value in [0, m-1] we are looking at
int i = 0;
while (i < n) {
int v = a[i];
int j = i;
while (j < n && a[j] == v) ++j;
int cnt = j - i;
while (need < m && need < v) {
missing.push_back(need);
++need;
}
if (v < m && need == v) {
// reserve exactly one copy of v to realize v itself
++need;
for (int k = 1; k < cnt; ++k) leftover.push_back(v);
} else {
for (int k = 0; k < cnt; ++k) leftover.push_back(v);
}
i = j;
}
while (need < m) {
missing.push_back(need);
++need;
}
int p = 0;
int sz = (int)leftover.size();
for (int x : missing) {
int req = 2 * x + 1;
while (p < sz && leftover[p] < req) ++p;
if (p == sz) return false;
++p;
}
return true;
};
int lo = 0, hi = n + 1; // mex is at most n
while (hi - lo > 1) {
int mid = (lo + hi) / 2;
if (can(mid)) lo = mid;
else hi = mid;
}
cout << lo << '\n';
}
}#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 T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n);
for (int &x : a) cin >> x;
sort(a.begin(), a.end());
auto can = [&](int m) -> bool {
vector<int> missing;
vector<int> leftover;
missing.reserve(m);
leftover.reserve(n);
int need = 0; // next value in [0, m-1] we are looking at
int i = 0;
while (i < n) {
int v = a[i];
int j = i;
while (j < n && a[j] == v) ++j;
int cnt = j - i;
while (need < m && need < v) {
missing.push_back(need);
++need;
}
if (v < m && need == v) {
// reserve exactly one copy of v to realize v itself
++need;
for (int k = 1; k < cnt; ++k) leftover.push_back(v);
} else {
for (int k = 0; k < cnt; ++k) leftover.push_back(v);
}
i = j;
}
while (need < m) {
missing.push_back(need);
++need;
}
int p = 0;
int sz = (int)leftover.size();
for (int x : missing) {
int req = 2 * x + 1;
while (p < sz && leftover[p] < req) ++p;
if (p == sz) return false;
++p;
}
return true;
};
int lo = 0, hi = n + 1; // mex is at most n
while (hi - lo > 1) {
int mid = (lo + hi) / 2;
if (can(mid)) lo = mid;
else hi = mid;
}
cout << lo << '\n';
}
}