| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| using int64 = long long; |
|
|
| static const int64 QUERY_LIMIT = 50000; |
|
|
| int n; |
| long long k; |
|
|
| |
| vector<long long> cacheVal; |
| vector<unsigned char> cacheSeen; |
|
|
| inline size_t idx(int x, int y) { |
| return (size_t)x * (n + 1) + (size_t)y; |
| } |
|
|
| long long ask(int x, int y) { |
| cout << "QUERY " << x << " " << y << endl; |
| cout.flush(); |
| long long v; |
| if (!(cin >> v)) { |
| |
| exit(0); |
| } |
| |
| if ((int)cacheVal.size() >= (n + 1) * (n + 1)) { |
| size_t id = idx(x, y); |
| cacheVal[id] = v; |
| cacheSeen[id] = 1; |
| } |
| return v; |
| } |
|
|
| long long getCached(int x, int y) { |
| size_t id = idx(x, y); |
| if (!cacheSeen[id]) { |
| long long v = ask(x, y); |
| cacheVal[id] = v; |
| cacheSeen[id] = 1; |
| return v; |
| } |
| return cacheVal[id]; |
| } |
|
|
| void done(long long ans) { |
| cout << "DONE " << ans << endl; |
| cout.flush(); |
| } |
|
|
| long long kthByMerge() { |
| struct Node { |
| long long val; |
| int i, j; |
| }; |
| struct Cmp { |
| bool operator()(const Node& a, const Node& b) const { |
| if (a.val != b.val) return a.val > b.val; |
| if (a.i != b.i) return a.i > b.i; |
| return a.j > b.j; |
| } |
| }; |
|
|
| priority_queue<Node, vector<Node>, Cmp> pq; |
|
|
| vector<long long> firstVal(n + 2, 0); |
| vector<unsigned char> firstKnown(n + 2, 0); |
| int nextRow = 1; |
|
|
| auto maybePushRows = [&]() { |
| if (pq.empty() && nextRow <= n) { |
| if (!firstKnown[nextRow]) { |
| firstVal[nextRow] = ask(nextRow, 1); |
| firstKnown[nextRow] = 1; |
| } |
| pq.push({firstVal[nextRow], nextRow, 1}); |
| nextRow++; |
| } |
| while (nextRow <= n) { |
| if (!firstKnown[nextRow]) { |
| firstVal[nextRow] = ask(nextRow, 1); |
| firstKnown[nextRow] = 1; |
| } |
| if (pq.empty()) { |
| pq.push({firstVal[nextRow], nextRow, 1}); |
| nextRow++; |
| } else { |
| long long topv = pq.top().val; |
| if (firstVal[nextRow] <= topv) { |
| pq.push({firstVal[nextRow], nextRow, 1}); |
| nextRow++; |
| } else break; |
| } |
| } |
| }; |
|
|
| long long popped = 0; |
| long long ans = 0; |
|
|
| maybePushRows(); |
|
|
| while (true) { |
| if (pq.empty()) { |
| |
| if (nextRow <= n) { |
| if (!firstKnown[nextRow]) { |
| firstVal[nextRow] = ask(nextRow, 1); |
| firstKnown[nextRow] = 1; |
| } |
| pq.push({firstVal[nextRow], nextRow, 1}); |
| nextRow++; |
| } else { |
| |
| break; |
| } |
| } |
|
|
| Node t = pq.top(); pq.pop(); |
| popped++; |
| if (popped == k) { |
| ans = t.val; |
| break; |
| } |
| if (t.j + 1 <= n) { |
| long long nv = ask(t.i, t.j + 1); |
| pq.push({nv, t.i, t.j + 1}); |
| } |
| |
| maybePushRows(); |
| } |
|
|
| return ans; |
| } |
|
|
| long long countLE(long long X) { |
| long long cnt = 0; |
| int i = n; |
| int j = 1; |
| while (i >= 1 && j <= n) { |
| long long v = getCached(i, j); |
| if (v <= X) { |
| cnt += i; |
| j++; |
| } else { |
| i--; |
| } |
| } |
| return cnt; |
| } |
|
|
| long long kthByBinary() { |
| |
| long long low = ask(1, 1); |
| long long high = ask(n, n); |
| |
| while (low < high) { |
| long long mid = low + ((high - low) >> 1); |
| long long c = countLE(mid); |
| if (c >= k) high = mid; |
| else low = mid + 1; |
| } |
| return low; |
| } |
|
|
| int main() { |
| ios::sync_with_stdio(false); |
| cin.tie(nullptr); |
|
|
| if (!(cin >> n >> k)) { |
| return 0; |
| } |
|
|
| |
| cacheVal.assign((size_t)(n + 1) * (n + 1), 0); |
| cacheSeen.assign((size_t)(n + 1) * (n + 1), 0); |
|
|
| |
| const long long maxQueries = QUERY_LIMIT; |
|
|
| |
| if (k + n <= maxQueries) { |
| long long ans = kthByMerge(); |
| done(ans); |
| } else { |
| |
| |
| if (2LL * n * 60 + 2 <= maxQueries) { |
| long long ans = kthByBinary(); |
| done(ans); |
| } else { |
| |
| long long ans = kthByBinary(); |
| done(ans); |
| } |
| } |
|
|
| return 0; |
| } |