| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| struct Interactor { |
| int n; |
| long long qlimit = 50000; |
| long long used = 0; |
|
|
| vector<long long> val; |
| vector<uint8_t> 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) { |
| |
| |
| 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<int>* posOut = nullptr) { |
| vector<int> local; |
| vector<int>& 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<int>* posOut = nullptr) { |
| vector<int> local; |
| vector<int>& 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; |
| |
| double score; |
| if (cin >> score) { |
| |
| } |
| 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<MinNode, vector<MinNode>, greater<MinNode>> 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<MaxNode> 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; |
| }; |
|
|
| |
| 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); |
| } |
|
|
| |
| mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); |
|
|
| |
| int rBound = int((k + n - 1) / n); |
| rBound = clampi(rBound, 1, n); |
| long long highTh = it.get(rBound, n); |
|
|
| vector<int> posHigh, posLow; |
| long long cntHigh = it.count_leq(highTh, &posHigh); |
|
|
| |
| 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; |
|
|
| 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<MinNode, vector<MinNode>, greater<MinNode>> 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<MaxNode> 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) { |
| |
| long long mx = it.get(n, n); |
| if (mx != highTh) { |
| highTh = mx; |
| cntHigh = it.count_leq(highTh, &posHigh); |
| } |
| if (cntHigh < k) { |
| |
| break; |
| } |
| } |
|
|
| long long candTotal = cntHigh - cntLow; |
| if (candTotal <= 0) { |
| |
| |
| it.done(highTh); |
| } |
|
|
| long long needSmall = k - cntLow; |
| long long needLarge = cntHigh - k + 1; |
|
|
| |
| vector<long long> 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 (remaining < (long long)2 * n + SAMPLES + 200) { |
| |
| if ((long long)nonempty + min(needSmall, needLarge) + 10 <= remaining) { |
| long long ans = enumerate_answer(needSmall, needLarge); |
| it.done(ans); |
| } else { |
| |
| it.done(highTh); |
| } |
| } |
|
|
| |
| vector<long long> samp; |
| samp.reserve(SAMPLES); |
| uniform_int_distribution<long long> 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<int> 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<int> posPivot; |
| long long cntPivot = it.count_leq(pivot, &posPivot); |
|
|
| if (cntPivot >= k) { |
| |
| if (pivot == highTh && cntPivot == cntHigh) { |
| |
| vector<int> posLess; |
| long long cntLess = it.count_lt(pivot, &posLess); |
| if (cntLess < k) { |
| it.done(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 { |
| |
| lowTh = pivot; |
| posLow.swap(posPivot); |
| cntLow = cntPivot; |
| } |
| } |
|
|
| |
| it.done(0); |
| } |