Problem: cf1275_f
Difficulty: medium
Categories: search
Time limit: 2000 ms, memory limit: 512 MB

This is an interactive problem.

When data becomes too large and no longer fits on a single server, it must be sharded. Consider a system for storing user posts distributed across $S$ servers, numbered starting from one. Each time a user writes a post, it is assigned a unique identifier ranging from $1$ to $10^{18}$ and saved to a random server. The later a post was created, the larger its $id$. Sometimes posts are deleted, so servers may contain significantly different numbers of posts.

Consider all undeleted post $id$s for a user and sort them in ascending order. You want to find the $k$-th smallest identifier among them. To do this, you can send no more than 100 additional queries. Each query has the format "? $i$ $j$". In response, you will receive the identifier of the $j$-th smallest post among those stored on the $i$-th server. When you believe you know the $k$-th smallest identifier, instead of a query, you must output the answer in the format "! $id$".

Interaction Protocol

The first line contains two integers $n$ and $S$ ($1 \le n \le 100, 1 \le S \le 5$) — the number of users for whom the problem must be solved independently, and the number of servers where the posts are stored.

Next, you must solve the problem $n$ times. At the beginning of each case, read $S$ integers $a_1, a_2, \dots, a_S$ ($0 \le a_i; \sum a_i \le 10^5$) — the number of posts the user has on each server. The next line will contain the integer $k$ ($1 \le k \le \sum a_i $) — the rank of the post identifier you need to find.

Then, perform no more than 100 queries in the format described in the problem statement.

Note that the limit on the number of queries applies to each user separately; the query counter is reset when moving to the next test case.

Example

Input
```
1 2
3 2
3

3

5

10
```

Output
```
? 1 2

? 2 1

? 1 3

! 5
```

Note
In the example, the first server stores posts with $id$s 1, 3, and 10. The second server stores 5 and 7. You need to find the 3rd smallest number, which is 5.

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static long long queries = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries << "\n";
        tout << "query_limit=" << query_limit << "\n";
        tout.flush();
    } catch (...) {
        
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

struct PQNode {
    long long val;
    int s;
    int pos;
};
struct PQCmp {
    bool operator()(const PQNode& a, const PQNode& b) const {
        if (a.val != b.val) return a.val > b.val;
        if (a.s != b.s) return a.s > b.s;
        return a.pos > b.pos;
    }
};

static long long kthSmallestMerge(const vector<vector<long long>>& ids, long long k) {
    priority_queue<PQNode, vector<PQNode>, PQCmp> pq;
    long long total = 0;
    for (int i = 0; i < (int)ids.size(); i++) {
        total += (long long)ids[i].size();
        if (!ids[i].empty()) pq.push({ids[i][0], i, 0});
    }
    if (k < 1 || k > total) return -1;

    long long last = -1;
    for (long long t = 0; t < k; t++) {
        if (pq.empty()) return -1;
        auto cur = pq.top();
        pq.pop();
        last = cur.val;
        int np = cur.pos + 1;
        if (np < (int)ids[cur.s].size()) {
            pq.push({ids[cur.s][np], cur.s, np});
        }
    }
    return last;
}

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);

    int n = inf.readInt(1, 100, "n");
    int S = inf.readInt(1, 5, "S");

    query_limit = 100LL * (long long)n;
    const long long hard_cap = max(200000LL, 10LL * query_limit);

    log_metrics(); 

    cout << n << " " << S << endl;

    for (int tc = 0; tc < n; tc++) {
        vector<int> a(S, 0);
        long long total = 0;
        for (int i = 0; i < S; i++) {
            a[i] = inf.readInt(0, 100000, "a_i");
            total += a[i];
        }
        if (total <= 0) finish(_pe, "Hidden input invalid: total posts is zero");
        if (total > 100000) finish(_pe, "Hidden input invalid: total posts too large");

        long long k = inf.readLong(1, total, "k");

        vector<vector<long long>> ids(S);
        for (int i = 0; i < S; i++) {
            ids[i].resize(a[i]);
            for (int j = 0; j < a[i]; j++) {
                long long x = inf.readLong(1LL, 1000000000000000000LL, "id");
                ids[i][j] = x;
            }
        }

        long long correct = kthSmallestMerge(ids, k);
        if (correct < 0) finish(_pe, "Failed to compute correct answer from hidden input");

        for (int i = 0; i < S; i++) {
            if (i) cout << ' ';
            cout << a[i];
        }
        cout << endl;
        cout << k << endl;

        while (true) {
            string cmd;
            if (!(cin >> cmd)) finish(_pe, "Unexpected EOF from contestant");

            if (cmd == "?") {
                long long si, sj;
                if (!(cin >> si >> sj)) finish(_pe, "Malformed query");
                if (si < 1 || si > S) finish(_pe, "Query server index out of range");
                int idx = (int)si - 1;
                if (sj < 1 || sj > (long long)a[idx]) finish(_pe, "Query position out of range");

                queries++;
                if (queries > hard_cap) finish(_pe, "Hard cap exceeded");

                long long ans = ids[idx][(int)sj - 1];
                cout << ans << endl;
                log_metrics();
            } else if (cmd == "!") {
                long long ans;
                if (!(cin >> ans)) finish(_pe, "Malformed answer");
                log_metrics();
                if (ans != correct) finish(_wa, "Wrong answer");
                break;
            } else {
                finish(_pe, "Unknown command (expected '?' or '!')");
            }
        }
    }

    string extra;
    if (cin >> extra) finish(_pe, "Extra output after finishing all test cases");
    finish(_ok, "OK");
    return 0;
}


Adaptive interactor (C++):

Test generator (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static const long long LIM = 1000000000000000000LL;

static vector<int> balancedCounts(int S, int total) {
    vector<int> a(S, total / S);
    for (int i = 0; i < total % S; i++) a[i]++;
    return a;
}

static vector<vector<long long>> makeInterleavedInstance(
    int S,
    const vector<int>& a,
    long long maxInc,
    long long startHint 
) {
    long long total = 0;
    for (int x : a) total += x;

    __int128 need = (__int128)total * (__int128)maxInc + 5;
    long long maxStart = (need >= LIM ? 1LL : (long long)((__int128)LIM - need));
    if (maxStart < 1) maxStart = 1;

    long long cur = (startHint >= 1 ? min(startHint, maxStart) : rnd.next(1LL, maxStart));
    vector<long long> all;
    all.reserve((size_t)total);

    for (long long i = 0; i < total; i++) {
        all.push_back(cur);
        long long inc = rnd.next(1LL, maxInc);
        __int128 nxt = (__int128)cur + (__int128)inc;
        if (nxt > LIM) nxt = LIM;
        cur = (long long)nxt;
        if (cur == LIM && i + 1 < total) {
            cur = LIM - (long long)(total - (i + 1));
        }
    }

    vector<int> perm(S);
    iota(perm.begin(), perm.end(), 0);
    for (int i = S - 1; i > 0; i--) {
        int j = rnd.next(0, i);
        swap(perm[i], perm[j]);
    }

    vector<vector<long long>> ids(S);
    for (int i = 0; i < S; i++) ids[i].reserve((size_t)a[i]);

    vector<int> used(S, 0);
    for (long long t = 0; t < total; t++) {
        int preferred = perm[(int)(t % S)];
        int s = preferred;
        if (used[s] >= a[s]) {
            s = -1;
            for (int i = 0; i < S; i++) {
                int cand = perm[i];
                if (used[cand] < a[cand]) {
                    s = cand;
                    break;
                }
            }
        }
        ids[s].push_back(all[(size_t)t]);
        used[s]++;
    }
    return ids;
}

static void printServerIdsLine(const vector<long long>& v) {
    for (int i = 0; i < (int)v.size(); i++) {
        if (i) cout << ' ';
        cout << v[i];
    }
    cout << "\n";
}

int main(int argc, char** argv) {
    registerGen(argc, argv, 1);

    string mode = opt<string>("mode", "non");
    int Sopt = opt<int>("S", 5);
    int nopt = opt<int>("n", 1);
    int maxA = opt<int>("maxA", 100000);
    long long maxInc = opt<long long>("maxInc", 1000000LL);

    long long seedValue = 0;
    if (argc >= 2) {
        seedValue = atoll(argv[1]);
        if (seedValue < 0) seedValue = -seedValue;
    }

    int S = min(max(Sopt, 1), 5);
    int n = max(nopt, 1);

    struct Case {
        vector<int> a;
        long long k;
        vector<vector<long long>> ids; 
    };

    vector<Case> cases;

    auto addCaseNon = [&](int Slocal, const vector<int>& a, long long k, const vector<vector<long long>>& ids) {
        Case c;
        c.a = a;
        c.k = k;
        c.ids = ids;
        cases.push_back(std::move(c));
    };
    auto addCaseAdp = [&](const vector<int>& a, long long k) {
        Case c;
        c.a = a;
        c.k = k;
        cases.push_back(std::move(c));
    };

    if (seedValue == 1) {
        
        S = 1;
        n = 1;
        vector<int> a = {1};
        long long k = 1;
        if (mode == "non") {
            vector<vector<long long>> ids(1);
            ids[0] = {LIM};
            addCaseNon(S, a, k, ids);
        } else {
            addCaseAdp(a, k);
        }
    } else if (seedValue == 2) {
        
        S = 5;
        n = 1;
        vector<int> a = {0, 1, 0, 1, 0};
        long long k = 2;
        if (mode == "non") {
            vector<vector<long long>> ids(5);
            ids[1] = {1};
            ids[3] = {LIM};
            addCaseNon(S, a, k, ids);
        } else {
            addCaseAdp(a, k);
        }
    } else if (seedValue == 3) {
        
        S = 5;
        n = 3;

        {
            int total = min(maxA, 100000);
            vector<int> a = balancedCounts(S, total);
            auto ids = makeInterleavedInstance(S, a, min(maxInc, 1000LL), -1);
            long long k = total / 2 + 1;
            if (mode == "non") addCaseNon(S, a, k, ids);
            else addCaseAdp(a, k);
        }
        {
            int total = min(maxA, 100000);
            vector<int> a(S, 0);
            a[0] = total;
            auto ids = makeInterleavedInstance(S, a, min(maxInc, 1000000LL), LIM - 20000000000LL);
            long long k = (long long)total;
            if (mode == "non") addCaseNon(S, a, k, ids);
            else addCaseAdp(a, k);
        }
        {
            vector<int> a = {1, 1, 1, 1, 1};
            vector<vector<long long>> ids(5);
            ids[0] = {2};
            ids[1] = {100};
            ids[2] = {10000};
            ids[3] = {1000000};
            ids[4] = {LIM - 1};
            long long k = 4;
            if (mode == "non") addCaseNon(S, a, k, ids);
            else addCaseAdp(a, k);
        }
    } else {
        
        S = min(max(S, 1), 5);
        n = 1;

        int total = min(maxA, 100000);
        if (total < S) total = S;

        vector<int> a = balancedCounts(S, total);

        long long tightInc = min(maxInc, 2000LL);
        auto ids = makeInterleavedInstance(S, a, tightInc, -1);

        long long kCenter = (long long)total / 2 + 1;
        long long wiggle = rnd.next(-2LL * (long long)S, 2LL * (long long)S);
        long long k = kCenter + wiggle;
        if (k < 1) k = 1;
        if (k > total) k = total;

        if (mode == "non") addCaseNon(S, a, k, ids);
        else addCaseAdp(a, k);
    }

    
    n = (int)cases.size();
    cout << n << " " << S << "\n";

    for (int tc = 0; tc < n; tc++) {
        const auto& a = cases[tc].a;
        for (int i = 0; i < S; i++) {
            if (i) cout << ' ';
            cout << (i < (int)a.size() ? a[i] : 0);
        }
        cout << "\n";
        cout << cases[tc].k << "\n";

        if (mode == "non") {
            const auto& ids = cases[tc].ids;
            for (int i = 0; i < S; i++) {
                if (i < (int)ids.size()) printServerIdsLine(ids[i]);
                else cout << "\n";
            }
        }
    }

    return 0;
}