Problem: cf1147_f
Difficulty: medium
Categories: graph, greedy, game
Time limit: 5000 ms, memory limit: 256 MB

You are given a complete bipartite graph with $$$2n$$$ nodes, with $$$n$$$ nodes on each side of the bipartition. Nodes $$$1$$$ through $$$n$$$ are on one side of the bipartition, and nodes $$$n+1$$$ to $$$2n$$$ are on the other side. You are also given an $$$n \times n$$$ matrix $$$a$$$ describing the edge weights. $$$a\_{ij}$$$ denotes the weight of the edge between nodes $$$i$$$ and $$$j+n$$$. Each edge has a distinct weight.

Alice and Bob are playing a game on this graph. First Alice chooses to play as either "increasing" or "decreasing" for herself, and Bob gets the other choice. Then she places a token on any node of the graph. Bob then moves the token along any edge incident to that node. They now take turns playing the following game, with Alice going first.

The current player must move the token from the current vertex to some adjacent unvisited vertex. Let $$$w$$$ be the last weight of the last edge that was traversed. The edge that is traversed must be strictly greater than $$$w$$$ if the player is playing as "increasing", otherwise, it must be strictly less. The first player unable to make a move loses.

You are given $$$n$$$ and the edge weights of the graph. You can choose to play as either Alice or Bob, and you will play against the judge. You must win all the games for your answer to be judged correct.

Interaction

Each test contains multiple test cases. The first line contains the number of test cases $$$t$$$ ($$$1 \le t \le 50$$$). Description of the test cases follows.

Each test starts with an integer $$$n$$$ ($$$1 \leq n \leq 50$$$) — the number of nodes on each side of the bipartition.

The next $$$n$$$ lines contain $$$n$$$ integers $$$a\_{ij}$$$ ($$$1 \leq a\_{ij} \leq n^2$$$). $$$a\_{ij}$$$ denotes the weight of the edge between node $$$i$$$ and node $$$j+n$$$. All $$$a\_{ij}$$$ will be distinct.

You first print a string "A" or "B", denoting which player you want to play ("A" for Alice and "B" for Bob).

If playing as Alice, first print either "I" or "D" (denoting whether you choose "increasing" or "decreasing"). Then, print the node that you wish to start at.

If playing as Bob, read in a character "I" or "D" (denoting whether the judge chose "increasing" or "decreasing"), then the node that the judge chooses to start at.

To make a move, print the index of the node that you wish to go to. To read what move the judge made, read an integer from standard in.

If the judge responds with $$$-1$$$, then that means the judge has determined it has no legal moves (or it may have just given up) and that you won the case. Stop processing this case immediately and start processing the next case.

If the judge responds with $$$-2$$$, that means that the judge has determined that you made an invalid move, so you should exit immediately to avoid getting other verdicts.

If you are unable to make a move or give up, print $$$-1$$$ and then exit immediately. This will give you a Wrong answer verdict.

After printing a move do not forget to output end of line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

* fflush(stdout) or cout.flush() in C++;
* System.out.flush() in Java;
* flush(output) in Pascal;
* stdout.flush() in Python;
* see documentation for other languages.

Example

Input

```
2
3
3 1 9
2 5 7
6 4 8
6
-1
1
1
I 1
-1
```

Output

```
A
D 3
2
B
2
```

Note

The first example has two test cases. In the first test case, the graph looks like the following.

 ![](https://espresso.codeforces.com/60ea39cd72634eb849b1d58172c900439124a303.png) 

In the sample output, the player decides to play as Alice and chooses "decreasing" and starting at node $$$3$$$. The judge responds by moving to node $$$6$$$. After, the player moves to node $$$2$$$. At this point, the judge has no more moves (since the weight must "increase"), so it gives up and prints $$$-1$$$.

In the next case, we have two nodes connected by an edge of weight $$$1$$$. The player decides to play as Bob. No matter what the judge chooses, the player can move the token to the other node and the judge has no moves so will lose.

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 string readTokenOrDie(const string& ctx) {
    string s;
    if (!(cin >> s)) finish(_pe, "Unexpected EOF while reading " + ctx);
    return s;
}

static bool parseIntStrict(const string& s, long long& out) {
    if (s.empty()) return false;
    int i = 0;
    bool neg = false;
    if (s[0] == '-') {
        neg = true;
        i = 1;
        if ((int)s.size() == 1) return false;
    }
    long long val = 0;
    for (; i < (int)s.size(); ++i) {
        if (s[i] < '0' || s[i] > '9') return false;
        int d = s[i] - '0';
        if (val > (LLONG_MAX - d) / 10) return false;
        val = val * 10 + d;
    }
    out = neg ? -val : val;
    return true;
}

struct TestCase {
    int n = 0;
    vector<vector<int>> a; 

    
    vector<int> max1, max2, argMax;
    vector<int> min1, min2, argMin;

    bool isLeft(int v) const { return 1 <= v && v <= n; }

    int w(int u, int v) const {
        if (isLeft(u) && !isLeft(v)) return a[u][v - n];
        if (!isLeft(u) && isLeft(v)) return a[v][u - n];
        return -1;
    }

    void precomputeExtremes() {
        int N = 2 * n;
        max1.assign(N + 1, INT_MIN);
        max2.assign(N + 1, INT_MIN);
        argMax.assign(N + 1, -1);

        min1.assign(N + 1, INT_MAX);
        min2.assign(N + 1, INT_MAX);
        argMin.assign(N + 1, -1);

        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                int left = i;
                int right = n + j;
                int wt = a[i][j];

                auto updMax = [&](int node, int other, int val) {
                    if (val > max1[node]) {
                        max2[node] = max1[node];
                        max1[node] = val;
                        argMax[node] = other;
                    } else if (val > max2[node]) {
                        max2[node] = val;
                    }
                };
                auto updMin = [&](int node, int other, int val) {
                    if (val < min1[node]) {
                        min2[node] = min1[node];
                        min1[node] = val;
                        argMin[node] = other;
                    } else if (val < min2[node]) {
                        min2[node] = val;
                    }
                };

                updMax(left, right, wt);
                updMax(right, left, wt);
                updMin(left, right, wt);
                updMin(right, left, wt);
            }
        }
    }

    int exclMax(int node, int excludeNeighbor) const {
        if (argMax[node] == excludeNeighbor) return max2[node];
        return max1[node];
    }

    int exclMin(int node, int excludeNeighbor) const {
        if (argMin[node] == excludeNeighbor) return min2[node];
        return min1[node];
    }

    vector<pair<int,int>> legalMoves(int cur, char type, int lastW, const vector<char>& vis) const {
        vector<pair<int,int>> res;
        if (!(type == 'I' || type == 'D')) return res;
        bool curLeft = isLeft(cur);
        if (curLeft) {
            for (int j = 1; j <= n; ++j) {
                int v = n + j;
                if (vis[v]) continue;
                int wt = a[cur][j];
                if ((type == 'I' && wt > lastW) || (type == 'D' && wt < lastW)) res.push_back({v, wt});
            }
        } else {
            int rightIdx = cur - n;
            for (int i = 1; i <= n; ++i) {
                int v = i;
                if (vis[v]) continue;
                int wt = a[i][rightIdx];
                if ((type == 'I' && wt > lastW) || (type == 'D' && wt < lastW)) res.push_back({v, wt});
            }
        }
        return res;
    }

    pair<char,int> chooseAliceOpeningHeuristic() const {
        
        
        
        int N = 2 * n;
        for (int s = 1; s <= N; ++s) {
            bool ok = true;
            if (isLeft(s)) {
                for (int j = 1; j <= n; ++j) {
                    int u = n + j;
                    int wt = a[s][j];
                    int mx = exclMax(u, s);
                    if (mx <= wt) { ok = false; break; }
                }
            } else {
                int sj = s - n;
                for (int i = 1; i <= n; ++i) {
                    int u = i;
                    int wt = a[i][sj];
                    int mx = exclMax(u, s);
                    if (mx <= wt) { ok = false; break; }
                }
            }
            if (ok) return {'I', s};
        }

        for (int s = 1; s <= N; ++s) {
            bool ok = true;
            if (isLeft(s)) {
                for (int j = 1; j <= n; ++j) {
                    int u = n + j;
                    int wt = a[s][j];
                    int mn = exclMin(u, s);
                    if (mn >= wt) { ok = false; break; }
                }
            } else {
                int sj = s - n;
                for (int i = 1; i <= n; ++i) {
                    int u = i;
                    int wt = a[i][sj];
                    int mn = exclMin(u, s);
                    if (mn >= wt) { ok = false; break; }
                }
            }
            if (ok) return {'D', s};
        }

        return {'I', 1};
    }

    int pickInitialBobMoveFromStart(int start, char aliceType) const {
        
        
        bool startLeft = isLeft(start);
        int bestV = -1;
        int bestW = (aliceType == 'I') ? INT_MIN : INT_MAX;

        if (startLeft) {
            for (int j = 1; j <= n; ++j) {
                int v = n + j;
                int wt = a[start][j];
                if (aliceType == 'I') {
                    if (wt > bestW) { bestW = wt; bestV = v; }
                } else {
                    if (wt < bestW) { bestW = wt; bestV = v; }
                }
            }
        } else {
            int sj = start - n;
            for (int i = 1; i <= n; ++i) {
                int v = i;
                int wt = a[i][sj];
                if (aliceType == 'I') {
                    if (wt > bestW) { bestW = wt; bestV = v; }
                } else {
                    if (wt < bestW) { bestW = wt; bestV = v; }
                }
            }
        }
        return bestV;
    }

    int pickGreedyMove(int cur, char myType, char oppType, int lastW, vector<char>& vis) const {
        auto moves = legalMoves(cur, myType, lastW, vis);
        if (moves.empty()) return -1;

        int bestV = -1, bestWt = 0;
        int bestOpp = INT_MAX;

        for (auto [v, wt] : moves) {
            vis[v] = 1;
            int oppCount = (int)legalMoves(v, oppType, wt, vis).size();
            vis[v] = 0;

            bool better = false;
            if (oppCount < bestOpp) {
                better = true;
            } else if (oppCount == bestOpp) {
                if (bestV == -1) {
                    better = true;
                } else {
                    
                    if (oppType == 'I') {
                        if (wt > bestWt) better = true;
                        else if (wt == bestWt && v < bestV) better = true;
                    } else {
                        if (wt < bestWt) better = true;
                        else if (wt == bestWt && v < bestV) better = true;
                    }
                }
            }

            if (better) {
                bestOpp = oppCount;
                bestV = v;
                bestWt = wt;
            }
        }
        return bestV;
    }
};

static void sendInvalidAndDie(const string& msg) {
    cout << -2 << endl;
    finish(_wa, msg);
}

static void ensureQueryCap(long long hard_cap) {
    if (queries > hard_cap) finish(_pe, "Hard cap exceeded");
}

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

    int t = inf.readInt(1, 50);
    vector<TestCase> cases;
    cases.reserve(t);

    long long limitSum = 0;
    for (int tc = 0; tc < t; ++tc) {
        TestCase cs;
        cs.n = inf.readInt(1, 50);
        cs.a.assign(cs.n + 1, vector<int>(cs.n + 1, 0));
        for (int i = 1; i <= cs.n; ++i) {
            for (int j = 1; j <= cs.n; ++j) {
                cs.a[i][j] = inf.readInt(1, cs.n * cs.n);
            }
        }
        cs.precomputeExtremes();
        cases.push_back(std::move(cs));
        limitSum += (2LL * cases.back().n - 1);
    }

    query_limit = limitSum;
    log_metrics(); 

    cout << t << endl;

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

    for (int tc = 0; tc < t; ++tc) {
        const TestCase& cs = cases[tc];
        int n = cs.n;
        int N = 2 * n;

        cout << n << endl;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (j > 1) cout << ' ';
                cout << cs.a[i][j];
            }
            cout << endl;
        }

        string role = readTokenOrDie("role (A/B)");
        if (role != "A" && role != "B") sendInvalidAndDie("Invalid role token");

        vector<char> vis(N + 1, 0);
        int cur = -1;
        int lastW = 0; 

        auto oppositeType = [&](char c) -> char {
            if (c == 'I') return 'D';
            if (c == 'D') return 'I';
            return '?';
        };

        if (role == "A") {
            string typeTok = readTokenOrDie("Alice type (I/D)");
            if (typeTok.size() != 1 || (typeTok[0] != 'I' && typeTok[0] != 'D')) sendInvalidAndDie("Invalid type token");
            char aliceType = typeTok[0];

            long long startLL;
            if (!parseIntStrict(readTokenOrDie("start node"), startLL)) sendInvalidAndDie("Invalid start node token");
            if (startLL < 1 || startLL > N) sendInvalidAndDie("Start node out of range");
            int start = (int)startLL;

            vis[start] = 1;

            char bobType = oppositeType(aliceType);
            int bobMove = cs.pickInitialBobMoveFromStart(start, aliceType);
            if (bobMove < 1 || bobMove > N || vis[bobMove] || (cs.isLeft(start) == cs.isLeft(bobMove)))
                finish(_fail, "Interactor internal error selecting initial Bob move");

            cout << bobMove << endl;
            vis[bobMove] = 1;
            lastW = cs.w(start, bobMove);
            cur = bobMove;

            while (true) {
                
                string mvTok = readTokenOrDie("contestant move");
                long long mvLL;
                if (!parseIntStrict(mvTok, mvLL)) sendInvalidAndDie("Non-integer move token");
                if (mvLL == -1) finish(_wa, "Contestant gave up / lost a game");
                if (mvLL < 1 || mvLL > N) sendInvalidAndDie("Move out of range");
                int v = (int)mvLL;

                if (vis[v]) sendInvalidAndDie("Move to visited vertex");
                if (cs.isLeft(cur) == cs.isLeft(v)) sendInvalidAndDie("Move not along bipartite edge");

                int wt = cs.w(cur, v);
                if (aliceType == 'I') {
                    if (!(wt > lastW)) sendInvalidAndDie("Increasing constraint violated");
                } else {
                    if (!(wt < lastW)) sendInvalidAndDie("Decreasing constraint violated");
                }

                ++queries;
                ensureQueryCap(hard_cap);

                vis[v] = 1;
                lastW = wt;
                cur = v;

                
                int reply = cs.pickGreedyMove(cur, bobType, aliceType, lastW, vis);
                if (reply == -1) {
                    cout << -1 << endl; 
                    break;
                }

                int wt2 = cs.w(cur, reply);
                if (wt2 == -1 || vis[reply] || (cs.isLeft(cur) == cs.isLeft(reply)))
                    finish(_fail, "Interactor internal error selecting Bob move");
                if (bobType == 'I') {
                    if (!(wt2 > lastW)) finish(_fail, "Interactor violated increasing constraint");
                } else {
                    if (!(wt2 < lastW)) finish(_fail, "Interactor violated decreasing constraint");
                }

                cout << reply << endl;
                vis[reply] = 1;
                lastW = wt2;
                cur = reply;
            }
        } else { 
            
            auto [aliceType, start] = cs.chooseAliceOpeningHeuristic();
            char bobType = oppositeType(aliceType);

            cout << aliceType << " " << start << endl;
            if (start < 1 || start > N) finish(_fail, "Interactor internal error selecting start node");
            vis[start] = 1;
            cur = start;

            
            {
                string mvTok = readTokenOrDie("contestant initial Bob move");
                long long mvLL;
                if (!parseIntStrict(mvTok, mvLL)) sendInvalidAndDie("Non-integer initial move token");
                if (mvLL == -1) finish(_wa, "Contestant gave up / lost a game");
                if (mvLL < 1 || mvLL > N) sendInvalidAndDie("Initial move out of range");
                int v = (int)mvLL;

                if (vis[v]) sendInvalidAndDie("Initial move to visited vertex");
                if (cs.isLeft(cur) == cs.isLeft(v)) sendInvalidAndDie("Initial move not along bipartite edge");

                ++queries;
                ensureQueryCap(hard_cap);

                lastW = cs.w(cur, v);
                vis[v] = 1;
                cur = v;
            }

            while (true) {
                
                int reply = cs.pickGreedyMove(cur, aliceType, bobType, lastW, vis);
                if (reply == -1) {
                    cout << -1 << endl; 
                    break;
                }

                int wt2 = cs.w(cur, reply);
                if (wt2 == -1 || vis[reply] || (cs.isLeft(cur) == cs.isLeft(reply)))
                    finish(_fail, "Interactor internal error selecting Alice move");
                if (aliceType == 'I') {
                    if (!(wt2 > lastW)) finish(_fail, "Interactor violated increasing constraint");
                } else {
                    if (!(wt2 < lastW)) finish(_fail, "Interactor violated decreasing constraint");
                }

                cout << reply << endl;
                vis[reply] = 1;
                lastW = wt2;
                cur = reply;

                
                string mvTok = readTokenOrDie("contestant move");
                long long mvLL;
                if (!parseIntStrict(mvTok, mvLL)) sendInvalidAndDie("Non-integer move token");
                if (mvLL == -1) finish(_wa, "Contestant gave up / lost a game");
                if (mvLL < 1 || mvLL > N) sendInvalidAndDie("Move out of range");
                int v = (int)mvLL;

                if (vis[v]) sendInvalidAndDie("Move to visited vertex");
                if (cs.isLeft(cur) == cs.isLeft(v)) sendInvalidAndDie("Move not along bipartite edge");

                int wt = cs.w(cur, v);
                if (bobType == 'I') {
                    if (!(wt > lastW)) sendInvalidAndDie("Increasing constraint violated");
                } else {
                    if (!(wt < lastW)) sendInvalidAndDie("Decreasing constraint violated");
                }

                ++queries;
                ensureQueryCap(hard_cap);

                vis[v] = 1;
                lastW = wt;
                cur = v;
            }
        }
    }

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


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 readToken(string& out) {
    if (!(cin >> out)) return false;
    return true;
}

static bool parseIntStrict(const string& s, long long& x) {
    if (s.empty()) return false;
    int i = 0;
    bool neg = false;
    if (s[0] == '-') {
        neg = true;
        i = 1;
        if (i == (int)s.size()) return false;
    }
    long long val = 0;
    for (; i < (int)s.size(); ++i) {
        if (s[i] < '0' || s[i] > '9') return false;
        int d = s[i] - '0';
        if (val > (LLONG_MAX - d) / 10) return false;
        val = val * 10 + d;
    }
    x = neg ? -val : val;
    return true;
}

static long long readIntOrFail(const string& context) {
    string tok;
    if (!readToken(tok)) finish(_pe, "unexpected EOF while reading " + context);
    long long x;
    if (!parseIntStrict(tok, x)) finish(_pe, "non-integer token while reading " + context);
    return x;
}

struct CaseData {
    int n = 0;
    vector<vector<int>> a; 
};

static inline bool isLeft(int v, int n) { return 1 <= v && v <= n; }
static inline bool isRight(int v, int n) { return n + 1 <= v && v <= 2 * n; }
static inline int rightIndex(int v, int n) { return v - n; }

static int edgeWeight(int u, int v, int n, const vector<vector<int>>& a) {
    if (isLeft(u, n) && isRight(v, n)) return a[u][rightIndex(v, n)];
    if (isRight(u, n) && isLeft(v, n)) return a[v][rightIndex(u, n)];
    return -1;
}

static bool cmpOk(char mode, int w, int lastW) {
    if (mode == 'I') return w > lastW;
    return w < lastW; 
}

struct Vis {
    vector<char> L, R; 
    explicit Vis(int n = 0) : L(n + 1, 0), R(n + 1, 0) {}
    bool seen(int v, int n) const {
        if (isLeft(v, n)) return L[v];
        if (isRight(v, n)) return R[rightIndex(v, n)];
        return true;
    }
    void mark(int v, int n, bool val) {
        if (isLeft(v, n)) L[v] = (char)val;
        else if (isRight(v, n)) R[rightIndex(v, n)] = (char)val;
    }
};

static int countLegal(int cur, int lastW, char mode, int n, const vector<vector<int>>& a, const Vis& vis) {
    int cnt = 0;
    if (isLeft(cur, n)) {
        int i = cur;
        for (int j = 1; j <= n; ++j) {
            if (vis.R[j]) continue;
            int w = a[i][j];
            if (cmpOk(mode, w, lastW)) ++cnt;
        }
    } else {
        int j = rightIndex(cur, n);
        for (int i = 1; i <= n; ++i) {
            if (vis.L[i]) continue;
            int w = a[i][j];
            if (cmpOk(mode, w, lastW)) ++cnt;
        }
    }
    return cnt;
}

static vector<int> listLegal(int cur, int lastW, char mode, int n, const vector<vector<int>>& a, const Vis& vis) {
    vector<int> moves;
    if (isLeft(cur, n)) {
        int i = cur;
        for (int j = 1; j <= n; ++j) {
            if (vis.R[j]) continue;
            int w = a[i][j];
            if (cmpOk(mode, w, lastW)) moves.push_back(n + j);
        }
    } else {
        int j = rightIndex(cur, n);
        for (int i = 1; i <= n; ++i) {
            if (vis.L[i]) continue;
            int w = a[i][j];
            if (cmpOk(mode, w, lastW)) moves.push_back(i);
        }
    }
    return moves;
}



static int chooseBestConstrainedMove(int cur, int lastW, char judgeMode, char oppMode,
                                    int n, const vector<vector<int>>& a, Vis& vis) {
    vector<int> legal = listLegal(cur, lastW, judgeMode, n, a, vis);
    if (legal.empty()) return -1;

    int bestV = -1;
    int bestOpp = INT_MAX;

    for (int v : legal) {
        int w = edgeWeight(cur, v, n, a);
        vis.mark(v, n, true);
        int oppCnt = countLegal(v, w, oppMode, n, a, vis);
        vis.mark(v, n, false);

        if (oppCnt < bestOpp || (oppCnt == bestOpp && v < bestV)) {
            bestOpp = oppCnt;
            bestV = v;
        }
    }
    return bestV;
}



static int chooseBestUnconstrainedMove(int cur, char oppMode, int n, const vector<vector<int>>& a, Vis& vis) {
    vector<int> candidates;
    if (isLeft(cur, n)) {
        for (int j = 1; j <= n; ++j) if (!vis.R[j]) candidates.push_back(n + j);
    } else {
        for (int i = 1; i <= n; ++i) if (!vis.L[i]) candidates.push_back(i);
    }
    if (candidates.empty()) return -1;

    int bestV = -1;
    int bestOpp = INT_MAX;

    for (int v : candidates) {
        int w = edgeWeight(cur, v, n, a);
        vis.mark(v, n, true);
        int oppCnt = countLegal(v, w, oppMode, n, a, vis);
        vis.mark(v, n, false);

        if (oppCnt < bestOpp || (oppCnt == bestOpp && v < bestV)) {
            bestOpp = oppCnt;
            bestV = v;
        }
    }
    return bestV;
}


static pair<char,int> chooseAliceModeAndStart(int n, const vector<vector<int>>& a) {
    const long long NEG_INF = -(1LL<<60);

    auto evalAfterBobMove = [&](char aliceMode, int start, int bobMove) -> long long {
        char bobMode = (aliceMode == 'I') ? 'D' : 'I';
        Vis vis(n);
        vis.mark(start, n, true);
        vis.mark(bobMove, n, true);
        int cur = bobMove;
        int lastW = edgeWeight(start, bobMove, n, a);

        int best = chooseBestConstrainedMove(cur, lastW, aliceMode, bobMode, n, a, vis);
        if (best == -1) return NEG_INF;

        int w = edgeWeight(cur, best, n, a);
        vis.mark(best, n, true);
        int oppCnt = countLegal(best, w, bobMode, n, a, vis);
        vis.mark(best, n, false);

        return - (long long)oppCnt;
    };

    long long bestValue = NEG_INF;
    char bestMode = 'D';
    int bestStart = 1;

    for (char aliceMode : {'D', 'I'}) { 
        for (int start = 1; start <= 2 * n; ++start) {
            
            vector<int> bobMoves;
            if (isLeft(start, n)) {
                bobMoves.reserve(n);
                for (int j = 1; j <= n; ++j) bobMoves.push_back(n + j);
            } else {
                bobMoves.reserve(n);
                for (int i = 1; i <= n; ++i) bobMoves.push_back(i);
            }

            long long worstForUs = (1LL<<60);
            for (int bm : bobMoves) {
                long long v = evalAfterBobMove(aliceMode, start, bm);
                worstForUs = min(worstForUs, v);
            }

            if (worstForUs > bestValue ||
                (worstForUs == bestValue && aliceMode < bestMode) ||
                (worstForUs == bestValue && aliceMode == bestMode && start < bestStart)) {
                bestValue = worstForUs;
                bestMode = aliceMode;
                bestStart = start;
            }
        }
    }
    return {bestMode, bestStart};
}

int main(int argc, char** argv) {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    registerInteraction(argc, argv);
    atexit(log_metrics);

    int t = inf.readInt();
    if (t < 1 || t > 50) finish(_pe, "invalid t in input");

    vector<CaseData> cases;
    cases.reserve(t);

    for (int tc = 0; tc < t; ++tc) {
        int n = inf.readInt();
        if (n < 1 || n > 50) finish(_pe, "invalid n in input");
        CaseData cd;
        cd.n = n;
        cd.a.assign(n + 1, vector<int>(n + 1, 0));
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                int w = inf.readInt();
                cd.a[i][j] = w;
            }
        }
        cases.push_back(std::move(cd));
    }

    query_limit = 0;
    for (auto& cd : cases) query_limit += cd.n; 
    long long hard_cap = max(200000LL, 10LL * max(1LL, query_limit));

    
    cout << t << "\n";
    for (int tc = 0; tc < t; ++tc) {
        int n = cases[tc].n;
        cout << n << "\n";
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (j > 1) cout << ' ';
                cout << cases[tc].a[i][j];
            }
            cout << "\n";
        }
    }
    cout.flush();

    
    log_metrics();

    for (int tc = 0; tc < t; ++tc) {
        int n = cases[tc].n;
        const auto& a = cases[tc].a;

        string roleTok;
        if (!readToken(roleTok)) finish(_pe, "unexpected EOF before choosing A/B");
        if (roleTok != "A" && roleTok != "B") finish(_wa, "invalid role token (expected A/B)");

        Vis vis(n);
        int cur = -1;
        int lastW = 0;
        bool lastWDefined = false;

        char aliceMode = '?';
        char bobMode = '?';

        bool contestantIsAlice = (roleTok == "A");
        bool contestantTurn = false; 

        auto sendInt = [&](long long x) {
            cout << x << "\n";
            cout.flush();
        };

        auto invalidMove = [&](const string& why) {
            sendInt(-2);
            finish(_wa, why);
        };

        auto readMoveInt = [&](const string& ctx) -> long long {
            long long x = readIntOrFail(ctx);
            if (x == -2) finish(_pe, "participant printed -2 (reserved)");
            return x;
        };

        auto requireVertex = [&](long long x, const string& ctx) -> int {
            if (x < 1 || x > 2LL * n) invalidMove("vertex out of range in " + ctx);
            return (int)x;
        };

        auto checkOppositeSide = [&](int u, int v, const string& ctx) {
            bool ok = (isLeft(u, n) && isRight(v, n)) || (isRight(u, n) && isLeft(v, n));
            if (!ok) invalidMove("non-adjacent (same side) move in " + ctx);
        };

        auto checkUnvisited = [&](int v, const string& ctx) {
            if (vis.seen(v, n)) invalidMove("revisited vertex in " + ctx);
        };

        auto checkConstraint = [&](int u, int v, char mode, const string& ctx) {
            if (!lastWDefined) invalidMove("constraint checked without lastW in " + ctx);
            int w = edgeWeight(u, v, n, a);
            if (w < 0) invalidMove("invalid edge in " + ctx);
            if (!cmpOk(mode, w, lastW)) invalidMove("weight constraint violated in " + ctx);
        };

        auto applyMove = [&](int u, int v) {
            int w = edgeWeight(u, v, n, a);
            vis.mark(v, n, true);
            cur = v;
            lastW = w;
            lastWDefined = true;
        };

        if (contestantIsAlice) {
            
            string modeTok;
            if (!readToken(modeTok)) finish(_pe, "unexpected EOF while reading Alice mode");
            if (modeTok != "I" && modeTok != "D") finish(_wa, "invalid mode token (expected I/D)");
            aliceMode = modeTok[0];
            bobMode = (aliceMode == 'I') ? 'D' : 'I';

            long long s = readIntOrFail("starting node");
            int start = requireVertex(s, "starting node");
            vis.mark(start, n, true);
            cur = start;

            
            int judgeFirst = chooseBestUnconstrainedMove(cur, aliceMode, n, a, vis);
            if (judgeFirst == -1) {
                
                sendInt(-1);
                continue;
            }
            sendInt(judgeFirst);
            applyMove(cur, judgeFirst);

            
            contestantTurn = true;
        } else {
            
            auto [jMode, jStart] = chooseAliceModeAndStart(n, a);
            aliceMode = jMode;
            bobMode = (aliceMode == 'I') ? 'D' : 'I';

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

            vis.mark(jStart, n, true);
            cur = jStart;

            
            long long mv0 = readMoveInt("Bob initial move");
            if (mv0 == -1) finish(_wa, "participant gave up on initial move");
            int v0 = requireVertex(mv0, "Bob initial move");
            checkUnvisited(v0, "Bob initial move");
            checkOppositeSide(cur, v0, "Bob initial move");
            queries++;
            if (queries > hard_cap) finish(_pe, "too many moves (hard cap exceeded)");

            applyMove(cur, v0);

            
            contestantTurn = false;
        }

        
        while (true) {
            if (contestantTurn) {
                long long mv = readMoveInt("move");
                if (mv == -1) finish(_wa, "participant gave up");
                int v = requireVertex(mv, "move");
                checkUnvisited(v, "move");
                checkOppositeSide(cur, v, "move");
                char mode = contestantIsAlice ? aliceMode : bobMode;
                checkConstraint(cur, v, mode, "move");

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

                applyMove(cur, v);
                contestantTurn = false;
            } else {
                
                char judgeMode = contestantIsAlice ? bobMode : aliceMode;
                char oppMode = contestantIsAlice ? aliceMode : bobMode;

                int judgeMove = chooseBestConstrainedMove(cur, lastW, judgeMode, oppMode, n, a, vis);
                if (judgeMove == -1) {
                    sendInt(-1); 
                    break;
                }
                sendInt(judgeMove);
                applyMove(cur, judgeMove);
                contestantTurn = true;
            }
        }
    }

    finish(_ok, "ok"); 
}


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

static vector<int> randomPermutation(int n) {
    vector<int> p(n);
    iota(p.begin(), p.end(), 0);
    shuffleVec(p);
    return p;
}

static vector<vector<int>> genSnake(int n) {
    vector<vector<int>> a(n, vector<int>(n, 0));
    int cur = 1;
    for (int i = 0; i < n; ++i) {
        if (i % 2 == 0) {
            for (int j = 0; j < n; ++j) a[i][j] = cur++;
        } else {
            for (int j = n - 1; j >= 0; --j) a[i][j] = cur++;
        }
    }
    return a;
}

static vector<vector<int>> genRandomPermMatrix(int n) {
    int m = n * n;
    vector<int> w(m);
    iota(w.begin(), w.end(), 1);
    shuffleVec(w);

    vector<vector<int>> a(n, vector<int>(n, 0));
    int k = 0;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            a[i][j] = w[k++];
    return a;
}

static vector<vector<int>> genCheckerboardLowHigh(int n) {
    int m = n * n;
    vector<int> pos0, pos1;
    pos0.reserve((m + 1) / 2);
    pos1.reserve(m / 2);

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            int idx = i * n + j;
            if (((i + j) & 1) == 0) pos0.push_back(idx);
            else pos1.push_back(idx);
        }
    }

    shuffleVec(pos0);
    shuffleVec(pos1);

    vector<int> flat(m, 0);
    int cur = 1;
    for (int idx : pos0) flat[idx] = cur++;
    for (int idx : pos1) flat[idx] = cur++;

    vector<vector<int>> a(n, vector<int>(n, 0));
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            a[i][j] = flat[i * n + j];
    return a;
}

static vector<vector<int>> genPermutedSnake(int n) {
    vector<vector<int>> base = genSnake(n);
    vector<int> rp = randomPermutation(n);
    vector<int> cp = randomPermutation(n);

    vector<vector<int>> a(n, vector<int>(n, 0));
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            a[i][j] = base[rp[i]][cp[j]];
    return a;
}

static long long gcdll(long long a, long long b) {
    while (b) {
        long long t = a % b;
        a = b;
        b = t;
    }
    return a;
}

static vector<vector<int>> genAffinePermutationMatrix(int n) {
    long long m = 1LL * n * n;
    long long mul = 1;
    long long add = rnd.next(0LL, m - 1);

    for (int attempts = 0; attempts < 100000; ++attempts) {
        long long cand = rnd.next(1LL, m - 1);
        if (gcdll(cand, m) == 1) {
            mul = cand;
            break;
        }
    }

    vector<vector<int>> a(n, vector<int>(n, 0));
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            long long idx = 1LL * i * n + j;
            long long w = (idx * mul + add) % m + 1;
            a[i][j] = (int)w;
        }
    }
    return a;
}

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

    string mode = opt<string>("mode", "non");
    int nOpt = opt<int>("n", 50);
    long long seedVal = 0;
    if (argc > 1) {
        try {
            seedVal = stoll(string(argv[1]));
        } catch (...) {
            seedVal = 0;
        }
    }

    int n = nOpt;
    if (n < 1) n = 1;
    if (n > 50) n = 50;

    vector<vector<int>> a;

    if (seedVal == 1) {
        n = 1;
        a.assign(1, vector<int>(1, 1));
    } else if (seedVal == 2) {
        n = 2;
        a = {
            {1, 4},
            {3, 2}
        };
    } else if (seedVal == 3) {
        n = 50;
        a = genSnake(n);
    } else {
        if (n < 45) n = 45; 
        int type = rnd.next(0, 3);
        if (type == 0) a = genRandomPermMatrix(n);
        else if (type == 1) a = genCheckerboardLowHigh(n);
        else if (type == 2) a = genPermutedSnake(n);
        else a = genAffinePermutationMatrix(n);
    }

    (void)mode; 
    if (mode != "non" && mode != "adp") mode = "non";

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