#include using namespace std; struct Interactor { int n; long long qlimit = 50000; long long used = 0; vector val; vector vis; Interactor(int n_) : n(n_) { size_t sz = size_t(n + 1) * size_t(n + 1); val.assign(sz, 0); vis.assign(sz, 0); } inline size_t idx(int x, int y) const { return size_t(x) * size_t(n + 1) + size_t(y); } long long get(int x, int y) { size_t id = idx(x, y); if (vis[id]) return val[id]; if (used >= qlimit) { // Out of queries; terminate to avoid undefined behavior. // Best-effort: output something. cout << "DONE 0\n" << flush; exit(0); } cout << "QUERY " << x << ' ' << y << '\n' << flush; long long v; if (!(cin >> v)) exit(0); used++; vis[id] = 1; val[id] = v; return v; } long long count_leq(long long x, vector* posOut = nullptr) { vector local; vector& pos = posOut ? *posOut : local; pos.assign(n + 1, 0); long long cnt = 0; int j = n; for (int i = 1; i <= n; i++) { while (j >= 1) { long long v = get(i, j); if (v <= x) break; --j; } pos[i] = j; cnt += j; if (j == 0) { for (int ii = i + 1; ii <= n; ii++) pos[ii] = 0; break; } } return cnt; } long long count_lt(long long x, vector* posOut = nullptr) { vector local; vector& pos = posOut ? *posOut : local; pos.assign(n + 1, 0); long long cnt = 0; int j = n; for (int i = 1; i <= n; i++) { while (j >= 1) { long long v = get(i, j); if (v < x) break; --j; } pos[i] = j; cnt += j; if (j == 0) { for (int ii = i + 1; ii <= n; ii++) pos[ii] = 0; break; } } return cnt; } [[noreturn]] void done(long long ans) { cout << "DONE " << ans << '\n' << flush; // The interactor prints a score afterwards; read it if present. double score; if (cin >> score) { // ignore } exit(0); } }; struct MinNode { long long v; int r, c, R; bool operator>(const MinNode& other) const { return v > other.v; } }; struct MaxNode { long long v; int r, c, L; bool operator<(const MaxNode& other) const { return v < other.v; } }; static inline int clampi(int x, int lo, int hi) { if (x < lo) return lo; if (x > hi) return hi; return x; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; long long k; if (!(cin >> n >> k)) return 0; Interactor it(n); long long N2 = 1LL * n * n; if (k <= 1) { long long ans = it.get(1, 1); it.done(ans); } if (k >= N2) { long long ans = it.get(n, n); it.done(ans); } auto merge_rows_smallest = [&](long long t) -> long long { priority_queue, greater> pq; pq = decltype(pq)(); for (int i = 1; i <= n; i++) { long long v = it.get(i, 1); pq.push({v, i, 1, n}); } long long ans = 0; for (long long step = 1; step <= t; step++) { auto cur = pq.top(); pq.pop(); ans = cur.v; if (step == t) break; if (cur.c < cur.R) { int nc = cur.c + 1; long long nv = it.get(cur.r, nc); pq.push({nv, cur.r, nc, cur.R}); } } return ans; }; auto merge_rows_largest = [&](long long t) -> long long { priority_queue pq; for (int i = 1; i <= n; i++) { long long v = it.get(i, n); pq.push({v, i, n, 1}); } long long ans = 0; for (long long step = 1; step <= t; step++) { auto cur = pq.top(); pq.pop(); ans = cur.v; if (step == t) break; if (cur.c > cur.L) { int nc = cur.c - 1; long long nv = it.get(cur.r, nc); pq.push({nv, cur.r, nc, cur.L}); } } return ans; }; // If k (or from the top) is small enough, do direct k-way merge on rows. long long tSmall = min(k, N2 - k + 1); if (tSmall + n <= 49000) { long long ans; if (k <= N2 - k + 1) ans = merge_rows_smallest(k); else ans = merge_rows_largest(N2 - k + 1); it.done(ans); } // Rank-narrowing + partial enumeration within (low, high] using row segments. mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); // Upper threshold: max of row ceil(k/n) is guaranteed >= k-th. int rBound = int((k + n - 1) / n); rBound = clampi(rBound, 1, n); long long highTh = it.get(rBound, n); vector posHigh, posLow; long long cntHigh = it.count_leq(highTh, &posHigh); // If for some reason cntHigh < k (shouldn't happen), expand to full max. if (cntHigh < k) { highTh = it.get(n, n); cntHigh = it.count_leq(highTh, &posHigh); } posLow.assign(n + 1, 0); long long cntLow = 0; long long lowTh = LLONG_MIN; // informational only auto can_enumerate = [&](long long needSmall, long long needLarge, int nonemptyRows) -> bool { long long remaining = it.qlimit - it.used; long long need = min(needSmall, needLarge); return (long long)nonemptyRows + need + 10 <= remaining; }; auto enumerate_answer = [&](long long needSmall, long long needLarge) -> long long { if (needSmall <= needLarge) { priority_queue, greater> pq; for (int i = 1; i <= n; i++) { int L = posLow[i] + 1; int R = posHigh[i]; if (L <= R) { long long v = it.get(i, L); pq.push({v, i, L, R}); } } long long ans = 0; for (long long step = 1; step <= needSmall; step++) { auto cur = pq.top(); pq.pop(); ans = cur.v; if (step == needSmall) break; if (cur.c < cur.R) { int nc = cur.c + 1; long long nv = it.get(cur.r, nc); pq.push({nv, cur.r, nc, cur.R}); } } return ans; } else { priority_queue pq; for (int i = 1; i <= n; i++) { int L = posLow[i] + 1; int R = posHigh[i]; if (L <= R) { long long v = it.get(i, R); pq.push({v, i, R, L}); } } long long ans = 0; for (long long step = 1; step <= needLarge; step++) { auto cur = pq.top(); pq.pop(); ans = cur.v; if (step == needLarge) break; if (cur.c > cur.L) { int nc = cur.c - 1; long long nv = it.get(cur.r, nc); pq.push({nv, cur.r, nc, cur.L}); } } return ans; } }; const int SAMPLES = 9; while (true) { if (cntHigh < k) { // Expand high to ensure correctness. long long mx = it.get(n, n); if (mx != highTh) { highTh = mx; cntHigh = it.count_leq(highTh, &posHigh); } if (cntHigh < k) { // Should never happen; fail-safe. break; } } long long candTotal = cntHigh - cntLow; if (candTotal <= 0) { // If interval collapsed, answer is <= highTh and > lowTh. // As a safe fallback, output highTh. it.done(highTh); } long long needSmall = k - cntLow; long long needLarge = cntHigh - k + 1; // Build prefix lengths and count non-empty rows. vector pref(n + 1, 0); int nonempty = 0; for (int i = 1; i <= n; i++) { int len = posHigh[i] - posLow[i]; if (len > 0) nonempty++; pref[i] = pref[i - 1] + max(0, len); } candTotal = pref[n]; if (candTotal <= 0) it.done(highTh); if (can_enumerate(needSmall, needLarge, nonempty)) { long long ans = enumerate_answer(needSmall, needLarge); it.done(ans); } long long remaining = it.qlimit - it.used; // If we can't afford another narrowing step and still enumerate, try again with enumeration anyway. if (remaining < (long long)2 * n + SAMPLES + 200) { // Best effort: attempt enumeration from whichever side is cheaper anyway. if ((long long)nonempty + min(needSmall, needLarge) + 10 <= remaining) { long long ans = enumerate_answer(needSmall, needLarge); it.done(ans); } else { // As a last resort, output highTh (likely incorrect, but prevents UB). it.done(highTh); } } // Sample pivot values from candidate segments (posLow+1 .. posHigh) uniformly by position. vector samp; samp.reserve(SAMPLES); uniform_int_distribution distPick(1, candTotal); for (int s = 0; s < SAMPLES; s++) { long long pick = distPick(rng); int row = int(lower_bound(pref.begin() + 1, pref.end(), pick) - pref.begin()); int len = posHigh[row] - posLow[row]; if (len <= 0) { s--; continue; } uniform_int_distribution distCol(1, len); int col = posLow[row] + distCol(rng); samp.push_back(it.get(row, col)); } sort(samp.begin(), samp.end()); double q = double(needSmall) / double(candTotal); int idx = int(q * double(SAMPLES - 1)); idx = clampi(idx, 0, SAMPLES - 1); long long pivot = samp[idx]; vector posPivot; long long cntPivot = it.count_leq(pivot, &posPivot); if (cntPivot >= k) { // Tighten high if (pivot == highTh && cntPivot == cntHigh) { // Can't shrink; handle duplicates by moving below pivot if needed. vector posLess; long long cntLess = it.count_lt(pivot, &posLess); if (cntLess < k) { it.done(pivot); } // Exclude pivot and above: new high threshold is < pivot. if (pivot == LLONG_MIN) { it.done(pivot); } highTh = pivot - 1; posHigh.swap(posLess); cntHigh = cntLess; } else { highTh = pivot; posHigh.swap(posPivot); cntHigh = cntPivot; } } else { // Tighten low lowTh = pivot; posLow.swap(posPivot); cntLow = cntPivot; } } // Fallback: should not reach. it.done(0); }