Problem: cf1267_i
Difficulty: medium
Categories: greedy, data structures
Time limit: 5000 ms, memory limit: 512 MB

This is an interactive problem.

You are the head coach of a chess club. The club has $2n$ players, each player has some strength which can be represented by a number, and all those numbers are distinct. The strengths of the players are not known to you.

You need to select $n$ players who would represent your club in the upcoming championship. Naturally, you want to select $n$ players with the highest strengths.

You can organize matches between the players to do that. In every match, you pick two players, they play some games, and you learn which one of the two has higher strength. You can wait for the outcome of a match before deciding who will participate in the next one.

However, you do not want to know exactly how those $n$ players compare between themselves, as that would make the championship itself less intriguing. More formally, you must reach a state where there is exactly one way to choose $n$ players with the highest strengths that is consistent with the outcomes of the matches you organized, but there must be at least two possible orderings of those $n$ players by strength that are consistent with the outcomes of the matches you organized.

Interaction

Your program has to process multiple test cases in one run. First, it should read the integer $t$ ($t \ge 1$) — the number of test cases. Then, it should process the test cases one by one.

In each test case, your program should start by reading the integer $n$ ($3 \le n \le 100$) — the number of players to select out of $2n$ players. The sum of squares of the values of $n$ over all test cases does not exceed $10\,000$.

Then your program can organize matches zero or more times. To organize a match, your program should print a match description formatted as ? $i$ $j$ — a question mark followed by two distinct numbers of players participating in the match. The players are numbered from 1 to $2n$, inclusive. Remember to flush the output after printing the match description. Then your program should read the match outcome — it will be either the greater-than character (>), if the first player in the match description has higher strength, or the less-than character (<), if the second player in the match description has higher strength.

Your program can organize at most $4n^2$ matches. After it is done organizing matches, it should print the exclamation mark (!) and continue to the next test case, or exit gracefully if this was the last test case. Remember to flush the output after printing the exclamation mark.

There must be exactly one way to choose $n$ players with the highest strength that is consistent with the outcomes of the matches you organized, but there must be at least two possible orderings of those $n$ players by their strength that are consistent with the outcomes of the matches you organized.

The judging program picks some distinct numbers as the strengths of all players before your program starts organizing matches and uses them to answer the requests.

Example

Input

```
2
3

>

<

>

<

>

>

3

<

<

<

>

>
```

Output

```
? 1 3

? 4 2

? 4 5

? 6 5

? 3 4

? 5 6

!

? 3 4

? 4 2

? 5 3

? 6 4

? 3 1

!
```

Note

In the example, the players in the first test case are sorted by strength in decreasing order. From the matches in the example output, we can deduce that players 1, 2, and 3 have the highest strength, but we do not know how the player 1 compares to the player 2.

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());
}

static bool parsePosInt(const string& s, int& x) {
    if (s.empty()) return false;
    long long v = 0;
    for (char c : s) {
        if (c < '0' || c > '9') return false;
        v = v * 10 + (c - '0');
        if (v > INT_MAX) return false;
    }
    x = (int)v;
    return true;
}

struct TestCase {
    int n = 0;
    int m = 0;
    vector<int> rank; 
};

static bool check_unique_top_and_ambiguous_order(int n, int m, vector< bitset<205> > g) {
    for (int k = 1; k <= m; ++k) {
        for (int i = 1; i <= m; ++i) {
            if (g[i].test(k)) g[i] |= g[k];
        }
        g[k].reset(k);
    }

    vector<int> outCnt(m + 1, 0), inCnt(m + 1, 0);
    for (int i = 1; i <= m; ++i) outCnt[i] = (int)g[i].count();
    for (int j = 1; j <= m; ++j) {
        int cnt = 0;
        for (int i = 1; i <= m; ++i) if (g[i].test(j)) ++cnt;
        inCnt[j] = cnt;
    }

    vector<int> top;
    top.reserve(n);
    vector<char> isTop(m + 1, 0);
    for (int i = 1; i <= m; ++i) {
        if (outCnt[i] >= n) {
            top.push_back(i);
            isTop[i] = 1;
        }
    }

    if ((int)top.size() != n) return false;

    for (int i = 1; i <= m; ++i) {
        if (!isTop[i]) {
            if (inCnt[i] < n) return false;
        }
    }

    bool ambiguous = false;
    for (int a = 0; a < n && !ambiguous; ++a) {
        for (int b = a + 1; b < n; ++b) {
            int u = top[a], v = top[b];
            if (!g[u].test(v) && !g[v].test(u)) {
                ambiguous = true;
                break;
            }
        }
    }
    return ambiguous;
}

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

    vector<TestCase> tests;
    int t = 0;
    try {
        t = inf.readInt();
        if (t < 1) finish(_pe, "Invalid hidden t");
        tests.resize(t);

        long long totalLimit = 0;
        for (int tc = 0; tc < t; ++tc) {
            int n = inf.readInt();
            if (n < 3 || n > 100) finish(_pe, "Invalid hidden n");
            int m = 2 * n;

            vector<int> p(m);
            for (int i = 0; i < m; ++i) p[i] = inf.readInt();

            vector<int> seen(m + 1, 0);
            for (int x : p) {
                if (x < 1 || x > m) finish(_pe, "Hidden permutation out of range");
                if (++seen[x] != 1) finish(_pe, "Hidden permutation has duplicates");
            }

            TestCase cs;
            cs.n = n;
            cs.m = m;
            cs.rank.assign(m + 1, -1);
            for (int i = 0; i < m; ++i) cs.rank[p[i]] = i;

            tests[tc] = std::move(cs);
            totalLimit += 4LL * n * n;
        }
        query_limit = totalLimit;
    } catch (const std::exception& e) {
        finish(_pe, string("Failed to read hidden input: ") + e.what());
    } catch (...) {
        finish(_pe, "Failed to read hidden input");
    }

    log_metrics(); 

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

    cout << t << endl;
    for (int tc = 0; tc < t; ++tc) {
        const int n = tests[tc].n;
        const int m = tests[tc].m;
        const vector<int>& rk = tests[tc].rank;

        cout << n << endl;

        vector< bitset<205> > g(m + 1);
        for (int i = 1; i <= m; ++i) g[i].reset();

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

            if (cmd == "!") {
                break;
            } else if (cmd == "?") {
                string si, sj;
                if (!(cin >> si >> sj)) finish(_pe, "Malformed query (missing indices)");

                int i = 0, j = 0;
                if (!parsePosInt(si, i) || !parsePosInt(sj, j)) finish(_pe, "Malformed query (non-integer indices)");
                if (i < 1 || i > m || j < 1 || j > m) finish(_pe, "Query index out of range");
                if (i == j) finish(_pe, "Query indices must be distinct");

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

                bool firstStronger = rk[i] < rk[j];
                if (firstStronger) {
                    g[i].set(j);
                    cout << ">" << endl;
                } else {
                    g[j].set(i);
                    cout << "<" << endl;
                }
            } else {
                finish(_pe, "Unknown command (expected '?' or '!')");
            }
        }

        if (!check_unique_top_and_ambiguous_order(n, m, g)) {
            finish(_wa, "State does not satisfy uniqueness/ambiguity requirements");
        }
    }

    finish(_ok, "OK");
}


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 Dinic {
    struct Edge {
        int to;
        int rev;
        long long cap;
    };
    int n;
    vector<vector<Edge>> g;
    vector<int> level;
    vector<int> it;

    explicit Dinic(int n_) : n(n_), g(n_), level(n_), it(n_) {}

    void addEdge(int fr, int to, long long cap) {
        Edge a{to, (int)g[to].size(), cap};
        Edge b{fr, (int)g[fr].size(), 0};
        g[fr].push_back(a);
        g[to].push_back(b);
    }

    bool bfs(int s, int t) {
        fill(level.begin(), level.end(), -1);
        queue<int> q;
        level[s] = 0;
        q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (const auto& e : g[v]) {
                if (e.cap > 0 && level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    q.push(e.to);
                }
            }
        }
        return level[t] >= 0;
    }

    long long dfs(int v, int t, long long f) {
        if (v == t) return f;
        for (int& i = it[v]; i < (int)g[v].size(); i++) {
            Edge& e = g[v][i];
            if (e.cap <= 0 || level[e.to] != level[v] + 1) continue;
            long long ret = dfs(e.to, t, min(f, e.cap));
            if (ret > 0) {
                e.cap -= ret;
                g[e.to][e.rev].cap += ret;
                return ret;
            }
        }
        return 0;
    }

    long long maxflow(int s, int t) {
        long long flow = 0;
        while (bfs(s, t)) {
            fill(it.begin(), it.end(), 0);
            while (true) {
                long long pushed = dfs(s, t, (long long)4e18);
                if (pushed == 0) break;
                flow += pushed;
            }
        }
        return flow;
    }
};

static bool parsePosInt(const string& s, int& out) {
    if (s.empty()) return false;
    long long val = 0;
    for (char c : s) {
        if (c < '0' || c > '9') return false;
        val = val * 10 + (c - '0');
        if (val > INT_MAX) return false;
    }
    out = (int)val;
    return true;
}

static const int MAXM = 205;

static long long deltaIfAdd(int m, const vector<bitset<MAXM>>& reach, int u, int v) {
    bitset<MAXM> add = reach[v];
    add.set(v);
    long long delta = 0;
    for (int x = 1; x <= m; x++) {
        if (x == u || reach[x].test(u)) {
            bitset<MAXM> newly = add & (~reach[x]);
            delta += (long long)newly.count();
        }
    }
    return delta;
}

static void addEdgeTransitive(int m, vector<bitset<MAXM>>& reach, int u, int v) {
    bitset<MAXM> add = reach[v];
    add.set(v);
    for (int x = 1; x <= m; x++) {
        if (x == u || reach[x].test(u)) {
            reach[x] |= add;
        }
    }
}

static long long maxIdealSizeExcludingForbidden(
    int m,
    const bitset<MAXM>& forbidden,
    const vector<pair<int,int>>& closureEdges 
) {
    const long long PEN = 1000000LL;
    int S = 0;
    int T = m + 1;
    Dinic dinic(m + 2);

    long long totalPos = 0;
    for (int v = 1; v <= m; v++) {
        if (forbidden.test(v)) {
            dinic.addEdge(v, T, PEN);
        } else {
            dinic.addEdge(S, v, 1);
            totalPos++;
        }
    }
    for (auto [a, b] : closureEdges) {
        if (a == b) continue;
        dinic.addEdge(a, b, PEN);
    }

    long long mincut = dinic.maxflow(S, T);
    long long maxWeight = totalPos - mincut;
    if (maxWeight < 0) maxWeight = 0;
    if (maxWeight > totalPos) maxWeight = totalPos;
    return maxWeight;
}

static bool checkFinishedState(
    int n,
    int m,
    const vector<bitset<MAXM>>& reach,
    const vector<pair<int,int>>& closureEdges
) {
    vector<int> alwaysIn;
    alwaysIn.reserve(m);
    for (int v = 1; v <= m; v++) {
        bitset<MAXM> forbidden = reach[v];
        forbidden.set(v);
        long long maxBefore = maxIdealSizeExcludingForbidden(m, forbidden, closureEdges);
        if (maxBefore <= n - 1) alwaysIn.push_back(v);
    }

    if ((int)alwaysIn.size() != n) return false;

    bool hasIncomparable = false;
    for (int i = 0; i < n && !hasIncomparable; i++) {
        for (int j = i + 1; j < n; j++) {
            int a = alwaysIn[i], b = alwaysIn[j];
            if (!reach[a].test(b) && !reach[b].test(a)) {
                hasIncomparable = true;
                break;
            }
        }
    }
    return hasIncomparable;
}

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

    int t = inf.readInt(1, 100000, "t");
    vector<int> ns(t);
    for (int tc = 0; tc < t; tc++) {
        ns[tc] = inf.readInt(3, 100, "n");
        query_limit += 4LL * ns[tc] * ns[tc];
    }

    long long hard_cap = max(200000LL, 10LL * query_limit);
    log_metrics(); 

    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cout << t << "\n";
    cout.flush();

    for (int tc = 0; tc < t; tc++) {
        int n = ns[tc];
        int m = 2 * n;

        bool fixedInstance = false;
        vector<int> posIndex(m + 1, -1);

        if (t == 1 && tc == 0 && !inf.seekEof()) {
            vector<int> p(m);
            vector<int> seen(m + 1, 0);
            for (int i = 0; i < m; i++) {
                int x = inf.readInt(1, m, "perm_i");
                if (++seen[x] != 1) finish(_pe, "invalid permutation in input");
                p[i] = x;
            }
            for (int i = 0; i < m; i++) posIndex[p[i]] = i;
            fixedInstance = true;
        }

        vector<bitset<MAXM>> reach(m + 1);
        vector<vector<unsigned char>> direct(m + 1, vector<unsigned char>(m + 1, 0));
        vector<pair<int,int>> closureEdges;
        closureEdges.reserve(4 * n * n);

        cout << n << "\n";
        cout.flush();

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

            if (cmd == "!") {
                break;
            }
            if (cmd != "?") finish(_pe, "invalid command token");

            string si, sj;
            if (!(cin >> si >> sj)) finish(_pe, "malformed query");
            int i, j;
            if (!parsePosInt(si, i) || !parsePosInt(sj, j)) finish(_pe, "non-integer query indices");
            if (i < 1 || i > m || j < 1 || j > m || i == j) finish(_pe, "query indices out of range");

            queries++;
            if (queries > hard_cap) finish(_pe, "too many queries (hard cap exceeded)");

            bool iStronger;
            if (fixedInstance) {
                if (posIndex[i] < 0 || posIndex[j] < 0) finish(_pe, "internal: missing posIndex");
                iStronger = (posIndex[i] < posIndex[j]);
            } else {
                if (reach[i].test(j)) {
                    iStronger = true;
                } else if (reach[j].test(i)) {
                    iStronger = false;
                } else {
                    long long d1 = deltaIfAdd(m, reach, i, j);
                    long long d2 = deltaIfAdd(m, reach, j, i);
                    if (d1 != d2) iStronger = (d1 < d2);
                    else iStronger = (i < j);
                }
            }

            int u = iStronger ? i : j;
            int v = iStronger ? j : i;

            if (reach[v].test(u)) finish(_pe, "internal: contradiction/cycle detected");

            if (!reach[u].test(v)) {
                if (!direct[u][v]) {
                    direct[u][v] = 1;
                    closureEdges.push_back({v, u}); 
                }
                addEdgeTransitive(m, reach, u, v);
            }

            cout << (iStronger ? ">" : "<") << "\n";
            cout.flush();
        }

        if (!checkFinishedState(n, m, reach, closureEdges)) {
            finish(_wa, "conditions not satisfied at '!'");
        }
    }

    finish(_ok, "OK");
    return 0;
}


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

static void shuffleVec(vector<int>& v) {
    for (int i = (int)v.size() - 1; i > 0; --i) {
        int j = rnd.next(0, i);
        swap(v[i], v[j]);
    }
}

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

    string mode = opt<string>("mode", "non");
    int forcedN = opt<int>("n", -1);

    int seed = 1;
    if (argc >= 2) {
        seed = atoi(argv[1]);
        if (seed <= 0) seed = 1;
    }

    int n = forcedN;
    if (n == -1) {
        if (mode == "adp") {
            if (seed == 1) n = 3;
            else if (seed == 2) n = 100;
            else if (seed == 3) n = 100;
            else n = rnd.next(90, 100);
        } else {
            if (seed == 1) n = 3;
            else if (seed == 2) n = 100;
            else if (seed == 3) n = 50;
            else {
                int roll = rnd.next(1, 10);
                if (roll == 1) n = rnd.next(3, 10);
                else n = rnd.next(80, 100);
            }
        }
    }

    n = max(3, min(100, n));
    int m = 2 * n;

    cout << 1 << "\n";
    cout << n << "\n";

    if (mode == "adp") {
        return 0;
    }

    vector<int> p;
    p.reserve(m);

    if (seed == 1) {
        
        
        
        
        p = {1, 3, 5, 6, 4, 2};
    } else if (seed == 2) {
        
        
        for (int x = m - 1; x >= 1; x -= 2) p.push_back(x);     
        for (int x = 2; x <= m; x += 2) p.push_back(x);         
    } else if (seed == 3) {
        
        
        vector<int> top, bot;
        for (int i = 1; i <= m; ++i) {
            if (i % 2 == 0) top.push_back(i);
            else bot.push_back(i);
        }
        
        int L = 0, R = (int)top.size() - 1;
        while (L <= R) {
            p.push_back(top[R--]);
            if (L <= R) p.push_back(top[L++]);
        }
        
        L = 0; R = (int)bot.size() - 1;
        while (L <= R) {
            p.push_back(bot[L++]);
            if (L <= R) p.push_back(bot[R--]);
        }
    } else {
        
        vector<int> topPlayers, bottomPlayers;
        topPlayers.reserve(n);
        bottomPlayers.reserve(n);

        for (int k = 1; k <= n; ++k) {
            int a = 2 * k - 1;
            int b = 2 * k;
            if (rnd.next(0, 1) == 0) {
                topPlayers.push_back(a);
                bottomPlayers.push_back(b);
            } else {
                topPlayers.push_back(b);
                bottomPlayers.push_back(a);
            }
        }

        
        shuffleVec(topPlayers);
        shuffleVec(bottomPlayers);

        
        
        vector<int> topRiffled;
        topRiffled.reserve(n);
        {
            vector<int> tmp = topPlayers;
            sort(tmp.begin(), tmp.end());
            int L = 0, R = (int)tmp.size() - 1;
            while (L <= R) {
                topRiffled.push_back(tmp[R--]);
                if (L <= R) topRiffled.push_back(tmp[L++]);
            }
            
            int shift = rnd.next(0, (int)topRiffled.size() - 1);
            rotate(topRiffled.begin(), topRiffled.begin() + shift, topRiffled.end());
        }

        
        vector<int> bottomRiffled;
        bottomRiffled.reserve(n);
        {
            vector<int> tmp = bottomPlayers;
            sort(tmp.begin(), tmp.end());
            int L = 0, R = (int)tmp.size() - 1;
            while (L <= R) {
                bottomRiffled.push_back(tmp[L++]);
                if (L <= R) bottomRiffled.push_back(tmp[R--]);
            }
            int shift = rnd.next(0, (int)bottomRiffled.size() - 1);
            rotate(bottomRiffled.begin(), bottomRiffled.begin() + shift, bottomRiffled.end());
        }

        for (int x : topRiffled) p.push_back(x);
        for (int x : bottomRiffled) p.push_back(x);
    }

    
    for (int i = 0; i < m; ++i) {
        if (i) cout << ' ';
        cout << p[i];
    }
    cout << "\n";
    return 0;
}