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.
When a query in base does not return -1, do not obsess over the whole digit sum. The reliable part is the congruence: the digit sum is congruent to modulo .
A -1 answer is not failure; it is a threshold test. Querying base cleanly tells you whether or .
For large , ask several bases whose values are pairwise coprime. Once their product exceeds , CRT pins down uniquely. CRT is the whole lockpick here.
Start with base . If , then bases give moduli , whose product is bigger than .
If the first query says , use the remaining queries as a tiny threshold tree: , then either , then either . In each final interval, combine the non--1 congruences and test the few possible candidates against the exact transcript.
Core observation
For any base , if the answer is not -1, it is the digit sum . Since , every power of is also , so
So every useful query gives us a modular equation. The exact digit sum may contain more information too, but the congruence is the dependable part.
Also, if the answer is -1, then . That means every query is both a modulus grabber and a threshold test. Pretty generous for an interactive problem, honestly.
The large-number branch
Ask base first.
If the answer is not -1, then . Now ask three more bases:
The moduli are:
They are pairwise coprime, and their product is
Since , the four congruences determine exactly one possible . Use CRT and we are done. No guessing, no vibes, no nonsense.
The small-number branch
If the first query returned -1, then . Now we only need to solve with three remaining queries.
Use this decision tree:
71874
if x < 71874:
33
if x < 33:
4
if x < 4: query 2, interval [1, 3]
else: query 8, interval [4, 7] or [8, 32]
else:
2178
if x < 2178: query 66, interval [33, 65] or [66, 2177]
else: query 66, interval [2178, 71873]The queried base itself tells us which interval we are in, because -1 means below that base and non--1 means at least that base.
How do we recover inside an interval?
Keep the full transcript: every pair (base, answer). For every non--1 answer, add the congruence
Combine those congruences with CRT. That gives all possible candidates in the interval as
where is the CRT modulus product. Then test each candidate by recomputing the exact answers for all queried bases. The correct is the one whose simulated transcript matches.
This is not brute force over , obviously. The candidate lists are tiny:
| final interval | useful moduli | candidates | |---|---:|---:| | | exact base check | at most | | | plus exact check | at most | | | plus exact check | at most | | | plus exact check | at most | | | plus exact check | at most | | | | exactly |
The exact transcript check is the clean way to avoid fragile casework. If two numbers share the same CRT residue in a tiny interval, their actual digit sums in one of the queried bases split them apart.
Correctness proof
First, every non--1 answer gives a true congruence , by the digit-sum identity above. Therefore the hidden number always satisfies the CRT system built from the transcript.
Second, every -1 answer gives a true inequality , while every non--1 answer gives . So the decision tree always places into the correct final interval.
Third, in the large branch, the product of the four pairwise-coprime moduli is greater than . Thus there is at most one integer in satisfying all four congruences. Since the hidden satisfies them, CRT recovers exactly .
Fourth, in the small branch, after CRT we enumerate only candidates inside the final interval that satisfy all modular constraints, then keep the one whose exact digit-sum answers match the full transcript. The hidden is included, and the construction above leaves no second matching candidate. So the recovered number is exactly .
We use at most four queries in every branch, so the strategy fits .
Complexity
Per game, the program makes at most queries and does constant-size CRT/candidate checking. The local computation is basically because digit sums divide by the base repeatedly. The interaction dominates everything.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Query {
ll b, a;
};
ll digitSum(ll x, ll b) {
if (x < b) return -1;
ll s = 0;
while (x > 0) {
s += x % b;
x /= b;
}
return s;
}
ll egcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll x1, y1;
ll g = egcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return g;
}
ll modInv(ll a, ll m) {
ll x, y;
egcd(a, m, x, y);
x %= m;
if (x < 0) x += m;
return x;
}
pair<__int128, __int128> buildCRT(const vector<Query> &qs) {
__int128 r = 0, mod = 1;
for (auto [b, a] : qs) {
ll m = b - 1;
if (a == -1 || m == 1) continue;
ll want = a % m;
ll have = (ll)(r % m);
ll delta = (want - have + m) % m;
ll coef = (ll)(mod % m);
ll t = (ll)((__int128)delta * modInv(coef, m) % m);
r += mod * t;
mod *= m;
r %= mod;
}
return {r, mod};
}
ll findAnswer(ll L, ll R, const vector<Query> &qs) {
auto [r, mod] = buildCRT(qs);
__int128 cur = r;
if (cur < L) {
__int128 need = (__int128)L - cur;
cur += ((need + mod - 1) / mod) * mod;
}
for (; cur <= R; cur += mod) {
ll x = (ll)cur;
bool ok = true;
for (auto [b, a] : qs) {
if (digitSum(x, b) != a) {
ok = false;
break;
}
}
if (ok) return x;
}
return L;
}
ll ask(ll b, vector<Query> &qs) {
cout << "? " << b << endl;
ll a;
if (!(cin >> a)) exit(0);
if (a == -2) exit(0);
qs.push_back({b, a});
return a;
}
void answer(ll x) {
cout << "! " << x << endl;
int r;
if (!(cin >> r)) exit(0);
if (r != 1) exit(0);
}
int main() {
setIO();
ll t, k, c;
cin >> t >> k >> c;
while (t--) {
vector<Query> qs;
ll a0 = ask(71874, qs);
ll ans;
if (a0 != -1) {
ask(71868, qs);
ask(71862, qs);
ask(71850, qs);
ans = findAnswer(71874, c, qs);
} else {
ll a33 = ask(33, qs);
if (a33 == -1) {
ll a4 = ask(4, qs);
if (a4 == -1) {
ask(2, qs);
ans = findAnswer(1, 3, qs);
} else {
ll a8 = ask(8, qs);
ans = findAnswer(4, a8 == -1 ? 7 : 32, qs);
}
} else {
ll a2178 = ask(2178, qs);
if (a2178 == -1) {
ll a66 = ask(66, qs);
ans = findAnswer(33, a66 == -1 ? 65 : 2177, qs);
} else {
ask(66, qs);
ans = findAnswer(2178, 71873, qs);
}
}
}
answer(ans);
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Query {
ll b, a;
};
ll digitSum(ll x, ll b) {
if (x < b) return -1;
ll s = 0;
while (x > 0) {
s += x % b;
x /= b;
}
return s;
}
ll egcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll x1, y1;
ll g = egcd(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return g;
}
ll modInv(ll a, ll m) {
ll x, y;
egcd(a, m, x, y);
x %= m;
if (x < 0) x += m;
return x;
}
pair<__int128, __int128> buildCRT(const vector<Query> &qs) {
__int128 r = 0, mod = 1;
for (auto [b, a] : qs) {
ll m = b - 1;
if (a == -1 || m == 1) continue;
ll want = a % m;
ll have = (ll)(r % m);
ll delta = (want - have + m) % m;
ll coef = (ll)(mod % m);
ll t = (ll)((__int128)delta * modInv(coef, m) % m);
r += mod * t;
mod *= m;
r %= mod;
}
return {r, mod};
}
ll findAnswer(ll L, ll R, const vector<Query> &qs) {
auto [r, mod] = buildCRT(qs);
__int128 cur = r;
if (cur < L) {
__int128 need = (__int128)L - cur;
cur += ((need + mod - 1) / mod) * mod;
}
for (; cur <= R; cur += mod) {
ll x = (ll)cur;
bool ok = true;
for (auto [b, a] : qs) {
if (digitSum(x, b) != a) {
ok = false;
break;
}
}
if (ok) return x;
}
return L;
}
ll ask(ll b, vector<Query> &qs) {
cout << "? " << b << endl;
ll a;
if (!(cin >> a)) exit(0);
if (a == -2) exit(0);
qs.push_back({b, a});
return a;
}
void answer(ll x) {
cout << "! " << x << endl;
int r;
if (!(cin >> r)) exit(0);
if (r != 1) exit(0);
}
int main() {
setIO();
ll t, k, c;
cin >> t >> k >> c;
while (t--) {
vector<Query> qs;
ll a0 = ask(71874, qs);
ll ans;
if (a0 != -1) {
ask(71868, qs);
ask(71862, qs);
ask(71850, qs);
ans = findAnswer(71874, c, qs);
} else {
ll a33 = ask(33, qs);
if (a33 == -1) {
ll a4 = ask(4, qs);
if (a4 == -1) {
ask(2, qs);
ans = findAnswer(1, 3, qs);
} else {
ll a8 = ask(8, qs);
ans = findAnswer(4, a8 == -1 ? 7 : 32, qs);
}
} else {
ll a2178 = ask(2178, qs);
if (a2178 == -1) {
ll a66 = ask(66, qs);
ans = findAnswer(33, a66 == -1 ? 65 : 2177, qs);
} else {
ask(66, qs);
ans = findAnswer(2178, 71873, qs);
}
}
}
answer(ans);
}
}