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

  1. Flush after every line. Use endl, cout << flush, or fflush(stdout). A forgotten flush gives Idleness Limit Exceeded.
  2. Don’t untie cin from cout unless you flush manually every time. The tie is what makes cin flush pending output.
  3. 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.
  4. 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

BudgetTechnique
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

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.

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.
  • 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; }
#endif

Stress the fake interactor over many hidden values, including adversarial ones. For a real two-process test: mkfifo f; ./sol < f | ./interactor > f.

SymptomCause
Idleness limitno flush
WA immediatelywrong format (?/! prefixes, spaces, 0- vs 1-based)
WA lateover budget, or off-by-one in the bounds
REkept reading after
Passes locally, fails on the judgeyour local interactor isn’t adaptive