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.
Do not compare sums. The sample already nukes that idea: Alice has total , Bob has total , and Alice still wins.
On a move, only the opponent's current maximum changes. That screams for thinking in terms of max-heaps, not sorted prefixes or total damage races.
Choosing a non-maximum element from your own array is never useful. Your chosen element is not consumed, so using a larger one only makes the opponent's array smaller.
Once both players always use their current maximum, the game has no branching left. Just simulate: current max attacks opponent max, and any positive leftover goes back.
The simulation is fast enough. In every two moves, either some element is destroyed, or the total sum drops by at least . So use two priority_queue<ll> heaps and alternate turns.
Comparing sums is bait. The first sample has Alice total and Bob total , but Alice wins anyway. So yeah, sums are lying to your face.
The Greedy Move Is Forced
Suppose it is Alice's turn. Bob's attacked element is fixed by the statement: it must be Bob's maximum element .
Alice only chooses the attack value from her own array.
If Alice has two choices , then using is always at least as good:
So Alice should always use her current maximum. Same exact argument applies to Bob.
This kills the scary minimax tree. The game is deterministic under optimal play: max attacks max, every turn.
Simulation
Maintain two max-heaps.
On Alice's turn:
Bob's turn is symmetric.
That is literally the whole algorithm. The only possible concern is whether this loop can run forever-ish because values go up to . It cannot.
Why The Simulation Is Fast
Let , and let be the current total sum of all alive elements.
Look at two consecutive moves.
If either move destroys an element, great. There are only elements total, so this can only happen times.
Otherwise, no element is destroyed in those two moves. Consider the first move in the pair. Let the attacker maximum be , and the opponent maximum be .
Since no destruction happens on the first move, we have . That means is the global maximum at the start of the pair.
After the first move, that element becomes . On the second move, if the attacker could destroy , we would be in the deletion case. Since we are not, the second attacker's maximum is less than . But it is at least , so:
Therefore:
The first move alone decreases the total sum by , which is more than half of the global maximum. Since the global maximum is at least , the sum drops by more than:
So every non-deleting pair of moves multiplies by at most . With total initial sum at most about , this gives only simulated moves. No billion-turn nonsense hiding under the bed.
Correctness Proof
First, on every turn, choosing the current maximum is optimal. Replacing any smaller chosen value with the current maximum leaves the player's own array unchanged and makes the opponent's array no larger. This cannot worsen the current player's position.
Second, because both players have this same dominant choice, optimal play is exactly the heap simulation described above.
Third, the simulation applies the statement's operation exactly: it always attacks the opponent's maximum, removes it if the attack is large enough, and otherwise reinserts the decreased value.
Finally, the first player whose move empties the opponent's heap is printed as the winner, exactly matching the win condition.
Complexity
Let and be the initial total sum.
The number of simulated moves is , and each heap operation costs .
So the total complexity is:
This easily fits for the given limits.
#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, m;
cin >> n >> m;
vector<ll> a(n), b(m);
for (ll &x : a) cin >> x;
for (ll &x : b) cin >> x;
priority_queue<ll> alice(a.begin(), a.end());
priority_queue<ll> bob(b.begin(), b.end());
bool aliceTurn = true;
while (true) {
if (aliceTurn) {
ll x = alice.top();
ll y = bob.top();
bob.pop();
if (y > x) bob.push(y - x);
if (bob.empty()) {
cout << "Alice\n";
break;
}
} else {
ll x = bob.top();
ll y = alice.top();
alice.pop();
if (y > x) alice.push(y - x);
if (alice.empty()) {
cout << "Bob\n";
break;
}
}
aliceTurn = !aliceTurn;
}
}
}#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, m;
cin >> n >> m;
vector<ll> a(n), b(m);
for (ll &x : a) cin >> x;
for (ll &x : b) cin >> x;
priority_queue<ll> alice(a.begin(), a.end());
priority_queue<ll> bob(b.begin(), b.end());
bool aliceTurn = true;
while (true) {
if (aliceTurn) {
ll x = alice.top();
ll y = bob.top();
bob.pop();
if (y > x) bob.push(y - x);
if (bob.empty()) {
cout << "Alice\n";
break;
}
} else {
ll x = bob.top();
ll y = alice.top();
alice.pop();
if (y > x) alice.push(y - x);
if (alice.empty()) {
cout << "Bob\n";
break;
}
}
aliceTurn = !aliceTurn;
}
}
}