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.
For a fixed value in , ignore the rest of the game for a second. Alice can remove this copy iff at least one value in divides . Bob can remove it iff at least one value in does not divide .
So every copy of a -value belongs to one of three buckets: Alice-only, Bob-only, or shared. The value is Alice-only if all divide it, Bob-only if no divides it, and shared otherwise.
To classify a value , compute : the number of elements of that divide , counting multiplicity. Then means Alice-only, means Bob-only, and means shared.
Because all values are at most , compute all with a divisor sweep: for each possible divisor , add to every multiple of .
Let , , be the counts of Alice-only, Bob-only, and shared copies in . Shared copies act like neutral turn tokens. If is even, Alice wins iff ; if is odd, Alice wins iff .
Research note: I checked the official Codeforces editorial page, the CF Step model solution, and the CF Step hints. The official tutorial text was not loaded there, but the available model code matches the three-bucket approach below. The proof here is reconstructed from the statement.
The key move is to stop thinking about individual choices of . For a fixed remaining copy of value in , define
This is the number of entries of that divide , with multiplicity.
Now classify every copy of every value in :
Let the total numbers of these copies be , , and .
At this point the original divisibility game becomes a tiny abstract game:
No Sprague-Grundy circus needed. The important false assumption to kill is: private tokens are not always good to spend early. If shared tokens exist, spending your private tokens can just make you run out sooner. Shared tokens are the contested tempo.
First solve the exclusive-only case, .
If Alice moves first and only remain, Alice wins iff . They alternate removing their own exclusive tokens. If , Alice runs out first or exactly together, and then it is Alice's turn with no move. If , Bob runs out first.
If Bob moves first in the same exclusive-only game, Alice wins iff , because now Bob is the one who must survive the equal-count race while moving first.
Shared tokens only decide whose turn starts that exclusive race. Since both players can always remove a shared token while , the first shared removals can be viewed as alternating neutral moves. Alice starts:
This gives the final rule:
For completeness, here is the induction version of the proof, since this is where bugs like to hide. Let mean Alice wins when it is Alice's turn, and mean Alice wins when it is Bob's turn. The claims are:
and
For , these are exactly the exclusive race above. For , a shared move flips the turn and changes only the parity of , so the condition transforms into the matching condition for the other player. A private move can only decrease your own future supply, so when the formula says you are losing, it cannot magically save you; checking the inequalities after decreasing or confirms that. That proves the rule.
Now compute the buckets efficiently.
Values satisfy . Let . Build frequency arrays and of size . To compute for every , sweep divisors:
The total work is
Then scan all with and add to the correct bucket by comparing with and .
Edge cases:
Complexity per test case is time and memory. Across all tests, the input sums keep this easily within 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;
int maxv = n + m;
vector<int> cntA(maxv + 1), cntB(maxv + 1), cover(maxv + 1);
for (int i = 0; i < n; ++i) {
int x;
cin >> x;
++cntA[x];
}
for (int i = 0; i < m; ++i) {
int x;
cin >> x;
++cntB[x];
}
for (int d = 1; d <= maxv; ++d) {
if (cntA[d] == 0) continue;
for (int y = d; y <= maxv; y += d) {
cover[y] += cntA[d];
}
}
ll aliceOnly = 0, bobOnly = 0, shared = 0;
for (int y = 1; y <= maxv; ++y) {
if (cntB[y] == 0) continue;
if (cover[y] == n) aliceOnly += cntB[y];
else if (cover[y] == 0) bobOnly += cntB[y];
else shared += cntB[y];
}
bool aliceWins = (shared % 2 == 0 ? aliceOnly > bobOnly : aliceOnly >= bobOnly);
if (aliceWins) cout << "Alice\n";
else cout << "Bob\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 T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
int maxv = n + m;
vector<int> cntA(maxv + 1), cntB(maxv + 1), cover(maxv + 1);
for (int i = 0; i < n; ++i) {
int x;
cin >> x;
++cntA[x];
}
for (int i = 0; i < m; ++i) {
int x;
cin >> x;
++cntB[x];
}
for (int d = 1; d <= maxv; ++d) {
if (cntA[d] == 0) continue;
for (int y = d; y <= maxv; y += d) {
cover[y] += cntA[d];
}
}
ll aliceOnly = 0, bobOnly = 0, shared = 0;
for (int y = 1; y <= maxv; ++y) {
if (cntB[y] == 0) continue;
if (cover[y] == n) aliceOnly += cntB[y];
else if (cover[y] == 0) bobOnly += cntB[y];
else shared += cntB[y];
}
bool aliceWins = (shared % 2 == 0 ? aliceOnly > bobOnly : aliceOnly >= bobOnly);
if (aliceWins) cout << "Alice\n";
else cout << "Bob\n";
}
return 0;
}