In an interactive problem your program talks to a judge program. The query limit is part of the statement, and it is the strongest hint about the intended algorithm.
The rules
- Flush after every line. Use
endl,cout << flush, orfflush(stdout). A forgotten flush gives Idleness Limit Exceeded. - Don’t untie
cinfromcoutunless you flush manually every time. The tie is what makescinflush pending output. - Stop on . After an invalid or excessive query most judges reply . Exit immediately, or the verdict turns into Idleness/RE and hides the real WA.
- Exceeding the query budget is WA, not TLE.
int ask(int l, int r) {
cout << "? " << l << ' ' << r << endl;
int x; cin >> x;
if (x == -1) exit(0);
return x;
}
void answer(int x) { cout << "! " << x << endl; }Budget to technique
| Budget | Technique |
|---|---|
| binary search; decision-tree lower bound is tight | |
| three-way splits (balance scales: each weighing has 3 outcomes) | |
| binary search plus verification, or two independent searches | |
| nested binary search, binary search on a tree or on HLD chains | |
| one pass, then a binary search; tournament for the second maximum ( comparisons) | |
| min and max together: compare in pairs |
Proof: the tournament finds the second maximum in comparisons
Run a single-elimination tournament: pair up elements, keep the larger, repeat — this is a binary tree with leaves and internal nodes (comparisons), found by induction: a tree on 1 leaf needs 0 comparisons, and merging two trees on leaves with comparisons needs one more comparison between their winners, giving . So the overall maximum costs comparisons, and the tree has depth , so the max played (and won) exactly matches on its path to the root, each against a distinct opponent — call this set , . The second-largest element is not the max, so it lost at least one match, say to . Since is second-largest, the only element larger than is the max itself, so (being larger than ) must be the max. Hence — every candidate for second place lost directly to the max. Finding the largest of the (at most) elements in by the same tournament argument costs more comparisons, for a total of .
Proof: pairwise comparison finds min and max together in comparisons
Process elements two at a time: compare each pair (1 comparison) to get a local min/max, then compare the local min against the running global min and the local max against the running global max (2 more comparisons) — 3 comparisons per pair, except the very first pair, where the local min/max comparison is the initial global min/max, so no extra 2 comparisons are needed there. With even: 1 pairing comparison for the first pair plus triples of 3 for the rest . With odd, seed the global min/max with the first single element (0 comparisons) and process the remaining (even) elements in pairs at 3 comparisons each: , which equals for odd (check: for odd , so , matching). This is a known tight bound (Knuth, TAOCP Vol. 3) — no comparison-based strategy does better; the matching adversary lower bound is a standard but longer argument, not reproduced here.
| | sorting with a comparison oracle (stable_sortwith the query as the comparator, or merge sort to bound comparisons) |
| | block decomposition, baby-step giant-step |
| | pairing arguments |
| | algebraic identity (sums, XORs, symmetric functions) |
Information-theoretic lower bound: with possible replies per query and possible hidden answers you need at least queries. If the budget equals that, every query must split the candidate set evenly.
Proof: the query lower bound
Model the interaction as a -ary decision tree: each internal node is one query, each of its children is one possible reply, and each leaf is a hidden answer the strategy can still distinguish. The strategy must end at a distinct leaf for each of the possible hidden answers (otherwise two different answers would receive the same final guess), so the tree needs at least leaves. A -ary tree of depth has at most leaves, so , giving , and since is an integer, . The depth is exactly the worst-case number of queries, so no strategy beats this bound. When the budget is exactly , equality forces every internal node to have (close to) leaves in each subtree, i.e. every query must split the remaining candidates as evenly as possible — an uneven split would make the larger branch need more than the remaining budget.
Adaptive judges
An adaptive interactor doesn’t fix the hidden answer in advance. It keeps every answer consistent with your queries so far and picks the one that hurts you most. Consequences:
- “Works on average” is not enough. Every query must guarantee progress in the worst case (an adversary argument).
- Randomised strategies still work against a judge that can’t see your random bits, as long as the bound holds in expectation for every fixed hidden answer. But if the statement says “adaptive”, assume the worst case.
- Ternary-style elimination needs care: guarantee that the candidate set shrinks by a constant factor for every possible reply.
Classic patterns
- Binary search on an unknown monotone predicate. Find the first “yes” in .
- Binary search with a lying oracle. With at most lies, repeat each query times and take the majority. Optimal strategies are Ulam’s game or Berlekamp’s volume bound.
Proof: majority of repeats is correct against at most lies
Repeating one query times gives replies, and the oracle can mark at most of them as lies (by hypothesis, across the whole interaction, so certainly within this one batch of repeats). So at least of the replies are truthful, and truthful replies to the same query all agree (the question has one correct answer), so the truthful answer appears at least times. Any other (lying) answer appears at most times in total across all repeats, hence strictly fewer than . So the truthful answer is a strict majority among the replies, and taking the majority recovers it correctly.
- Finding a hidden permutation with comparisons: insertion with binary search, or merge sort.
- Peak or local minimum in an array or grid with or queries: walk downhill on a recursively halved window.
- Guessing with XOR, AND or OR queries: recover each bit independently. turns two queries into sums.
- Tree centroid search: query the centroid of the remaining candidates, so the candidate count halves each time and queries suffice.
- Hidden graph edges: group testing, binary search on prefix sets of vertices (“is there an edge from into ?”).
Testing locally
Put a fake interactor in your own code behind #ifdef LOCAL. That tests the logic without any process plumbing:
#ifdef LOCAL
int hidden, used = 0;
int ask(int x) { assert(++used <= LIMIT); return x <= hidden; }
#else
int ask(int x) { cout << "? " << x << endl; int r; cin >> r; if (r == -1) exit(0); return r; }
#endifStress the fake interactor over many hidden values, including adversarial ones. For a real two-process test: mkfifo f; ./sol < f | ./interactor > f.
| Symptom | Cause |
|---|---|
| Idleness limit | no flush |
| WA immediately | wrong format (?/! prefixes, spaces, 0- vs 1-based) |
| WA late | over budget, or off-by-one in the bounds |
| RE | kept reading after |
| Passes locally, fails on the judge | your local interactor isn’t adaptive |