Problem: cf1991_e
Difficulty: medium
Categories: graph, greedy, game
Time limit: 2000 ms, memory limit: 256 MB

This is an interactive problem.

Consider an undirected connected graph consisting of $n$ vertices and $m$ edges. Each vertex can be colored with one of three colors: $1$, $2$, or $3$. Initially, all vertices are uncolored.

Alice and Bob are playing a game consisting of $n$ rounds. In each round, the following two-step process happens:

1. Alice chooses two different colors.
2. Bob chooses an uncolored vertex and colors it with one of the two colors chosen by Alice.

Alice wins if there exists an edge connecting two vertices of the same color. Otherwise, Bob wins.

You are given the graph. Your task is to decide which player you wish to play as and win the game.

Input

Each test contains multiple test cases. The first line contains a single integer $t$ ($1 \le t \le 1000$) — the number of test cases. The description of test cases follows.

The first line of each test case contains two integers $n$, $m$ ($1 \le n \le 10^4$, $n - 1 \le m \le \min(\frac{n \cdot (n - 1)}{2}, 10^4)$) — the number of vertices and the number of edges in the graph, respectively.

Each of the next $m$ lines of each test case contains two integers $u_i$, $v_i$ ($1 \le u_i, v_i \le n$) — the edges of the graph. It is guaranteed that the graph is connected and there are no multiple edges or self-loops.

It is guaranteed that the sum of $n$ and the sum of $m$ over all test cases does not exceed $10^4$.

Interaction

For each test case, you need to output a single line containing either "Alice" or "Bob", representing the player you choose.

Then for each of the following $n$ rounds, the following two-step process happens:

1. Alice (either you or the interactor) will output two integers $a$ and $b$ ($1 \le a, b \le 3$, $a \neq b$) — the colors chosen by Alice.
2. Bob (either you or the interactor) will output two integers $i$ and $c$ ($1 \le i \le n$, $c = a$ or $c = b$) — the vertex and the color chosen by Bob. Vertex $i$ must be a previously uncolored vertex.

If any of your outputs are invalid, the jury will output "-1" and you will receive a Wrong Answer verdict.

At the end of all $n$ turns, if you have lost the game, the jury will output "-1" and you will receive a Wrong Answer verdict.

If your program has received a $-1$ instead of a valid value, it must terminate immediately. Otherwise, you may receive an arbitrary verdict because your solution might be reading from a closed stream.

Note that if you are playing as Alice, and there already exists an edge connected two vertices of the same color, the interactor will not terminate early and you will keep playing all $n$ rounds.

After outputting, 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 2
2 3
3 1

3 1

2 2

1 1
4 4
1 2
2 3
3 4
4 1

2 3

1 2

2 1

3 1
```

Output

```
Alice
3 1

1 2

2 1

Bob

1 2

2 1

4 1

3 3
```

Note

Note that the sample test cases are example games and do not necessarily represent the optimal strategy for both players.

In the first test case, you choose to play as Alice.

1. Alice chooses two colors: $3$ and $1$. Bob chooses vertex $3$ and colors it with color $1$.
2. Alice chooses two colors: $1$ and $2$. Bob chooses vertex $2$ and colors it with color $2$.
3. Alice chooses two colors: $2$ and $1$. Bob chooses vertex $1$ and colors it with color $1$.

Alice wins because the edge $(3, 1)$ connects two vertices of the same color.

In the second test case, you choose to play as Bob.

1. Alice chooses two colors: $2$ and $3$. Bob chooses vertex $1$ and colors it with color $2$.
2. Alice chooses two colors: $1$ and $2$. Bob chooses vertex $2$ and colors it with color $1$.
3. Alice chooses two colors: $2$ and $1$. Bob chooses vertex $4$ and colors it with color $1$.
4. Alice chooses two colors: $3$ and $1$. Bob chooses vertex $3$ and colors it with color $3$.

Bob wins because there are no edges with vertices of the same color.

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 void send_minus_one_and_flush() {
    cout << "-1\n";
    cout.flush();
}

static bool readToken(string& s) {
    if (!(cin >> s)) return false;
    return true;
}

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

static bool readIntManual(long long& x) {
    string s;
    if (!readToken(s)) return false;
    return parseLL(s, x);
}

struct TestCaseData {
    int n = 0, m = 0;
    vector<pair<int,int>> edges;
    vector<vector<int>> adj;

    bool bip = true;
    vector<int> part; 
    vector<int> side0, side1;
};

static TestCaseData readCase() {
    TestCaseData tc;
    tc.n = inf.readInt();
    tc.m = inf.readInt();
    tc.edges.reserve(tc.m);
    tc.adj.assign(tc.n + 1, {});
    for (int i = 0; i < tc.m; i++) {
        int u = inf.readInt();
        int v = inf.readInt();
        if (u < 1 || u > tc.n || v < 1 || v > tc.n || u == v) {
            quitf(_fail, "Invalid hidden input edge.");
        }
        tc.edges.push_back({u, v});
        tc.adj[u].push_back(v);
        tc.adj[v].push_back(u);
    }

    tc.part.assign(tc.n + 1, -1);
    queue<int> q;
    tc.bip = true;
    for (int s = 1; s <= tc.n; s++) {
        if (tc.part[s] != -1) continue;
        tc.part[s] = 0;
        q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (int to : tc.adj[v]) {
                if (tc.part[to] == -1) {
                    tc.part[to] = tc.part[v] ^ 1;
                    q.push(to);
                } else if (tc.part[to] == tc.part[v]) {
                    tc.bip = false;
                }
            }
        }
    }

    if (tc.bip) {
        tc.side0.reserve(tc.n);
        tc.side1.reserve(tc.n);
        for (int i = 1; i <= tc.n; i++) {
            if (tc.part[i] == 0) tc.side0.push_back(i);
            else tc.side1.push_back(i);
        }
    }
    return tc;
}

static bool causesImmediateMono(const vector<vector<int>>& adj, const vector<int>& color, int v, int c) {
    for (int to : adj[v]) {
        if (color[to] == c) return true;
    }
    return false;
}

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

    try {
        int t = inf.readInt();
        if (t < 1 || t > 1000) quitf(_fail, "Invalid hidden t.");

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

        long long totalN = 0;
        for (int tc = 0; tc < t; tc++) {
            auto data = readCase();
            totalN += data.n;
            cases.push_back(std::move(data));
        }

        query_limit = totalN;
        log_metrics(); 

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

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

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

            cout << tc.n << " " << tc.m << "\n";
            for (auto [u, v] : tc.edges) {
                cout << u << " " << v << "\n";
            }
            cout.flush();

            string role;
            if (!readToken(role)) {
                finish(_pe, "Unexpected EOF while reading role.");
            }
            if (role != "Alice" && role != "Bob") {
                send_minus_one_and_flush();
                finish(_pe, "Invalid role (expected Alice/Bob).");
            }

            vector<int> color(tc.n + 1, 0);
            vector<char> colored(tc.n + 1, 0);
            int coloredCnt = 0;
            bool aliceWins = false;

            auto applyColor = [&](int v, int c) {
                colored[v] = 1;
                color[v] = c;
                coloredCnt++;
                for (int to : tc.adj[v]) {
                    if (color[to] == c) aliceWins = true;
                }
            };

            if (role == "Alice") {
                
                vector<int> st0, st1;
                if (tc.bip) {
                    st0 = tc.side0;
                    st1 = tc.side1;
                }
                vector<int> all;
                set<int> uncolored; 
                if (!tc.bip) {
                    for (int i = 1; i <= tc.n; i++) uncolored.insert(i);
                } else {
                    
                    for (int i = 1; i <= tc.n; i++) uncolored.insert(i);
                }

                for (int round = 1; round <= tc.n; round++) {
                    long long aLL, bLL;
                    if (!readIntManual(aLL) || !readIntManual(bLL)) {
                        finish(_pe, "Unexpected EOF while reading Alice move.");
                    }
                    int a = (int)aLL, b = (int)bLL;
                    if (!(1 <= a && a <= 3 && 1 <= b && b <= 3) || a == b) {
                        send_minus_one_and_flush();
                        finish(_pe, "Invalid Alice colors.");
                    }

                    int v = -1, c = -1;

                    if (tc.bip) {
                        bool has1 = (a == 1 || b == 1);
                        bool has2 = (a == 2 || b == 2);
                        bool has3 = (a == 3 || b == 3);

                        if (has1 && has2) {
                            if (!st0.empty()) { v = st0.back(); st0.pop_back(); c = 1; }
                            else if (!st1.empty()) { v = st1.back(); st1.pop_back(); c = 2; }
                        } else if (has1 && has3) {
                            if (!st0.empty()) { v = st0.back(); st0.pop_back(); c = 1; }
                            else if (!st1.empty()) { v = st1.back(); st1.pop_back(); c = 3; }
                        } else if (has2 && has3) {
                            if (!st1.empty()) { v = st1.back(); st1.pop_back(); c = 2; }
                            else if (!st0.empty()) { v = st0.back(); st0.pop_back(); c = 3; }
                        }

                        if (v == -1) {
                            
                            if (uncolored.empty()) {
                                send_minus_one_and_flush();
                                finish(_pe, "No uncolored vertices left (internal).");
                            }
                            v = *uncolored.begin();
                            c = a;
                        }
                    } else {
                        
                        int chosenV = -1, chosenC = -1;

                        auto tryPick = [&](int vtry) -> bool {
                            if (colored[vtry]) return false;
                            bool okA = !causesImmediateMono(tc.adj, color, vtry, a);
                            bool okB = !causesImmediateMono(tc.adj, color, vtry, b);
                            if (okA || okB) {
                                chosenV = vtry;
                                chosenC = okA ? a : b;
                                return true;
                            }
                            return false;
                        };

                        int scanned = 0;
                        for (int vtry : uncolored) {
                            if (tryPick(vtry)) break;
                            if (++scanned >= 64) break;
                        }
                        if (chosenV == -1) {
                            
                            for (int vtry : uncolored) {
                                if (tryPick(vtry)) break;
                            }
                        }
                        if (chosenV == -1) {
                            
                            if (uncolored.empty()) {
                                send_minus_one_and_flush();
                                finish(_pe, "No uncolored vertices left (internal).");
                            }
                            chosenV = *uncolored.begin();
                            chosenC = a;
                        }
                        v = chosenV;
                        c = chosenC;
                    }

                    if (v < 1 || v > tc.n || colored[v]) {
                        send_minus_one_and_flush();
                        finish(_pe, "Interactor selected invalid vertex (internal).");
                    }
                    if (!(c == a || c == b)) {
                        send_minus_one_and_flush();
                        finish(_pe, "Interactor selected invalid color (internal).");
                    }

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

                    
                    applyColor(v, c);
                    uncolored.erase(v);

                    queries++;
                    if (queries > hard_cap) {
                        send_minus_one_and_flush();
                        finish(_pe, "Hard cap exceeded.");
                    }
                }

                bool contestantWon = aliceWins;
                if (!contestantWon) {
                    send_minus_one_and_flush();
                    finish(_wa, "Contestant chose Alice and lost.");
                }
            } else {
                
                vector<char> used(tc.n + 1, 0);

                for (int round = 1; round <= tc.n; round++) {
                    int a = 1, b = 2;
                    if (!tc.bip) {
                        
                        a = 1; b = 2;
                    } else {
                        
                        if (round & 1) { a = 1; b = 3; }
                        else { a = 2; b = 3; }
                    }

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

                    long long vLL, cLL;
                    if (!readIntManual(vLL) || !readIntManual(cLL)) {
                        finish(_pe, "Unexpected EOF while reading Bob move.");
                    }
                    int v = (int)vLL, c = (int)cLL;

                    if (v < 1 || v > tc.n || used[v]) {
                        send_minus_one_and_flush();
                        finish(_pe, "Invalid vertex in Bob move.");
                    }
                    if (!(c == a || c == b)) {
                        send_minus_one_and_flush();
                        finish(_pe, "Invalid color in Bob move.");
                    }

                    used[v] = 1;
                    applyColor(v, c);

                    queries++;
                    if (queries > hard_cap) {
                        send_minus_one_and_flush();
                        finish(_pe, "Hard cap exceeded.");
                    }
                }

                bool contestantWon = !aliceWins;
                if (!contestantWon) {
                    send_minus_one_and_flush();
                    finish(_wa, "Contestant chose Bob and lost.");
                }
            }

            if (coloredCnt != tc.n) {
                send_minus_one_and_flush();
                finish(_pe, "Not all vertices were colored (internal).");
            }
        }

        finish(_ok, "OK");
    } catch (const exception& e) {
        finish(_pe, string("Exception: ") + e.what());
    } catch (...) {
        finish(_pe, "Unknown exception.");
    }
}


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 isAllSpace(const string& s) {
    for (unsigned char ch : s) if (!isspace(ch)) return false;
    return true;
}

static vector<long long> parseIntsFromLine(const string& line) {
    vector<long long> out;
    long long x;
    std::stringstream ss(line);
    while (ss >> x) out.push_back(x);
    return out;
}

static long long edgeKey(int u, int v) {
    if (u > v) swap(u, v);
    return (static_cast<long long>(u) << 20) ^ static_cast<long long>(v);
}

struct TestCase {
    int n = 0, m = 0;
    vector<pair<int,int>> edges;
    vector<vector<int>> adj;
    bool bip = true;
    vector<int> part[2];
};

static void addEdgeChecked(int n, unordered_set<long long>& used, vector<pair<int,int>>& edges, int u, int v) {
    if (u == v) return;
    if (u < 1 || u > n || v < 1 || v > n) return;
    long long k = edgeKey(u, v);
    if (used.find(k) != used.end()) return;
    used.insert(k);
    edges.push_back({u, v});
}

static TestCase buildGraphFromParams(int n, int m, int bipFlag) {
    TestCase tc;
    tc.n = n;
    int mMax = min(10000, n * (n - 1) / 2);
    m = max(0, min(mMax, m));
    if (n == 1) m = 0;
    else m = max(m, n - 1);
    bool wantBip = (bipFlag == 1);

    vector<pair<int,int>> edges;
    unordered_set<long long> used;
    used.reserve((size_t)m * 2 + 100);
    edges.reserve((size_t)m);

    if (n >= 2) {
        for (int i = 2; i <= n && (int)edges.size() < m; i++) addEdgeChecked(n, used, edges, i - 1, i);
    }

    if (!wantBip) {
        if (n >= 3) {
            if (m < n) m = n;
            if ((int)edges.size() < m) addEdgeChecked(n, used, edges, 1, 3); 
        } else {
            wantBip = true;
        }
    }

    auto wantCross = [&](int u, int v) -> bool {
        return ((u ^ v) & 1) == 1;
    };

    int u = 1, v = 2;
    while ((int)edges.size() < m) {
        if (n <= 1) break;
        if (u > n) break;
        if (v > n) {
            u++;
            v = u + 1;
            continue;
        }
        bool ok = true;
        if (wantBip) {
            if (!wantCross(u, v)) ok = false;
        }
        if (ok) addEdgeChecked(n, used, edges, u, v);
        v++;
    }

    
    if ((int)edges.size() < m && wantBip && n >= 2) {
        vector<int> odd, even;
        for (int i = 1; i <= n; i++) ((i & 1) ? odd : even).push_back(i);
        for (int a : odd) {
            for (int b : even) {
                if ((int)edges.size() >= m) break;
                addEdgeChecked(n, used, edges, a, b);
            }
            if ((int)edges.size() >= m) break;
        }
    }

    
    if ((int)edges.size() < m) {
        for (int a = 1; a <= n && (int)edges.size() < m; a++) {
            for (int b = a + 1; b <= n && (int)edges.size() < m; b++) {
                addEdgeChecked(n, used, edges, a, b);
            }
        }
    }

    if ((int)edges.size() > m) edges.resize(m);
    tc.m = (int)edges.size();
    tc.edges = std::move(edges);
    return tc;
}

static void buildAdjAndBip(TestCase& tc) {
    tc.adj.assign(tc.n + 1, {});
    tc.adj.shrink_to_fit();
    tc.adj.assign(tc.n + 1, {});
    for (auto [u, v] : tc.edges) {
        tc.adj[u].push_back(v);
        tc.adj[v].push_back(u);
    }

    vector<int> side(tc.n + 1, -1);
    queue<int> q;
    tc.bip = true;
    for (int i = 1; i <= tc.n; i++) {
        if (side[i] != -1) continue;
        side[i] = 0;
        q.push(i);
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (int y : tc.adj[x]) {
                if (side[y] == -1) {
                    side[y] = side[x] ^ 1;
                    q.push(y);
                } else if (side[y] == side[x]) {
                    tc.bip = false;
                }
            }
        }
    }
    tc.part[0].clear();
    tc.part[1].clear();
    tc.part[0].reserve(tc.n);
    tc.part[1].reserve(tc.n);
    for (int i = 1; i <= tc.n; i++) {
        int s = side[i];
        if (s < 0) s = 0;
        tc.part[s].push_back(i);
    }
    sort(tc.part[0].begin(), tc.part[0].end());
    sort(tc.part[1].begin(), tc.part[1].end());
}

static bool readToken(string& s) {
    if (!(cin >> s)) return false;
    return true;
}

static bool readIntSafe(int& x) {
    long long v;
    if (!(cin >> v)) return false;
    if (v < INT_MIN || v > INT_MAX) return false;
    x = (int)v;
    return true;
}

static void sendMinusOne() {
    cout << -1 << "\n";
    cout.flush();
}

struct BipBobPicker {
    const vector<int>* part0 = nullptr;
    const vector<int>* part1 = nullptr;
    vector<int>* color = nullptr;
    int p0 = 0, p1 = 0;

    int takeFrom(const vector<int>& part, int& ptr) {
        while (ptr < (int)part.size() && (*color)[part[ptr]] != 0) ptr++;
        if (ptr >= (int)part.size()) return -1;
        int v = part[ptr];
        ptr++;
        return v;
    }

    int hasFrom(const vector<int>& part, int ptr) {
        while (ptr < (int)part.size() && (*color)[part[ptr]] != 0) ptr++;
        return ptr < (int)part.size();
    }

    pair<int,int> pick(int a, int b) {
        int x = min(a, b), y = max(a, b);
        
        if (x == 1 && y == 2) {
            if (hasFrom(*part0, p0)) return {takeFrom(*part0, p0), 1};
            return {takeFrom(*part1, p1), 2};
        }
        if (x == 1 && y == 3) {
            if (hasFrom(*part0, p0)) return {takeFrom(*part0, p0), 1};
            return {takeFrom(*part1, p1), 3};
        }
        
        if (hasFrom(*part1, p1)) return {takeFrom(*part1, p1), 2};
        return {takeFrom(*part0, p0), 3};
    }
};

static bool isSafeColor(const TestCase& tc, const vector<int>& color, int v, int c) {
    for (int nei : tc.adj[v]) {
        if (color[nei] == c) return false;
    }
    return true;
}

static pair<int,int> pickGreedyBobMove(const TestCase& tc, const vector<int>& color, int a, int b, bool alreadyMono) {
    int n = tc.n;
    if (alreadyMono) {
        for (int v = 1; v <= n; v++) {
            if (color[v] == 0) return {v, a};
        }
        return {-1, a};
    }

    int bestV = -1;
    int bestC = -1;
    int bestSafeCount = 3;

    for (int v = 1; v <= n; v++) {
        if (color[v] != 0) continue;
        bool safeA = isSafeColor(tc, color, v, a);
        bool safeB = isSafeColor(tc, color, v, b);
        int sc = (int)safeA + (int)safeB;
        if (sc == 0) continue;
        if (sc < bestSafeCount || (sc == bestSafeCount && v < bestV)) {
            bestSafeCount = sc;
            bestV = v;
            if (sc == 1) bestC = safeA ? a : b;
            else bestC = min(a, b);
        }
    }

    if (bestV != -1) return {bestV, bestC};

    
    for (int v = 1; v <= n; v++) {
        if (color[v] == 0) return {v, min(a, b)};
    }
    return {-1, min(a, b)};
}

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

    
    string line;
    int t = 0;

    while (!inf.eof()) {
        line = inf.readLine();
        if (!isAllSpace(line)) break;
    }
    if (isAllSpace(line)) finish(_pe, "empty input");
    {
        auto v = parseIntsFromLine(line);
        if (v.size() != 1) finish(_pe, "bad t line");
        if (v[0] < 1 || v[0] > 1000) finish(_pe, "t out of range");
        t = (int)v[0];
    }

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

    for (int tcId = 0; tcId < t; tcId++) {
        string header;
        while (true) {
            if (inf.eof()) finish(_pe, "unexpected EOF in test header");
            header = inf.readLine();
            if (!isAllSpace(header)) break;
        }
        auto hv = parseIntsFromLine(header);
        if (!(hv.size() == 2 || hv.size() == 3)) finish(_pe, "bad test header arity");
        long long nll = hv[0], mll = hv[1];
        if (nll < 1 || nll > 10000) finish(_pe, "n out of range");
        int n = (int)nll;
        int mMax = min(10000, n * (n - 1) / 2);
        if (mll < 0 || mll > 10000) finish(_pe, "m out of range");
        int m = (int)mll;
        TestCase tc;
        if (hv.size() == 3) {
            int bipFlag = (int)hv[2];
            if (!(bipFlag == 0 || bipFlag == 1)) finish(_pe, "bipFlag out of range");
            tc = buildGraphFromParams(n, m, bipFlag);
        } else {
            tc.n = n;
            tc.m = m;
            tc.edges.clear();
            tc.edges.reserve((size_t)m);
            for (int i = 0; i < m; i++) {
                if (inf.eof()) finish(_pe, "unexpected EOF in edges");
                string eline = inf.readLine();
                if (isAllSpace(eline)) { i--; continue; }
                auto ev = parseIntsFromLine(eline);
                if (ev.size() != 2) finish(_pe, "bad edge line");
                int u = (int)ev[0], v = (int)ev[1];
                if (u < 1 || u > n || v < 1 || v > n || u == v) finish(_pe, "edge endpoint out of range");
                tc.edges.push_back({u, v});
            }
        }
        buildAdjAndBip(tc);
        cases.push_back(std::move(tc));
    }

    query_limit = 0;
    for (auto& tc : cases) query_limit += tc.n;
    if (query_limit < 0) query_limit = 0;
    log_metrics(); 

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

    
    cout << t << "\n";
    for (int tcId = 0; tcId < t; tcId++) {
        const auto& tc = cases[tcId];
        cout << tc.n << " " << (int)tc.edges.size() << "\n";
        for (auto [u, v] : tc.edges) cout << u << " " << v << "\n";
    }
    cout.flush();

    
    for (int tcId = 0; tcId < t; tcId++) {
        const TestCase& tc = cases[tcId];
        vector<int> color(tc.n + 1, 0);
        vector<char> used(tc.n + 1, 0);
        bool hasMono = false;

        string role;
        if (!readToken(role)) finish(_pe, "EOF while reading role");
        if (role != "Alice" && role != "Bob") {
            sendMinusOne();
            finish(_pe, "invalid role token");
        }

        bool contestantAlice = (role == "Alice");

        BipBobPicker bipBob;
        if (tc.bip) {
            bipBob.part0 = &tc.part[0];
            bipBob.part1 = &tc.part[1];
            bipBob.color = &color;
        }

        auto applyColorAndCheck = [&](int v, int c) {
            color[v] = c;
            used[v] = 1;
            for (int nei : tc.adj[v]) {
                if (color[nei] == c) hasMono = true;
            }
        };

        if (!contestantAlice) {
            
            for (int round = 1; round <= tc.n; round++) {
                if (++queries > hard_cap) {
                    sendMinusOne();
                    finish(_pe, "too many queries (hard cap)");
                }

                int a = 1, b = 2;
                if (!tc.bip) {
                    a = 1; b = 2; 
                } else {
                    int r = (round - 1) % 3;
                    if (r == 0) { a = 1; b = 2; }
                    else if (r == 1) { a = 1; b = 3; }
                    else { a = 2; b = 3; }
                }

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

                int i, c;
                if (!readIntSafe(i) || !readIntSafe(c)) {
                    finish(_pe, "EOF/malformed Bob move");
                }
                if (i < 1 || i > tc.n || used[i]) {
                    sendMinusOne();
                    finish(_pe, "invalid vertex index");
                }
                if (!(c == a || c == b)) {
                    sendMinusOne();
                    finish(_pe, "invalid color choice");
                }

                applyColorAndCheck(i, c);

                if (hasMono) {
                    sendMinusOne();
                    finish(_wa, "Bob created monochromatic edge");
                }
            }
            
            continue;
        } else {
            
            for (int round = 1; round <= tc.n; round++) {
                if (++queries > hard_cap) {
                    sendMinusOne();
                    finish(_pe, "too many queries (hard cap)");
                }

                int a, b;
                if (!readIntSafe(a) || !readIntSafe(b)) finish(_pe, "EOF/malformed Alice move");
                if (a < 1 || a > 3 || b < 1 || b > 3 || a == b) {
                    sendMinusOne();
                    finish(_pe, "invalid Alice colors");
                }

                pair<int,int> move = {-1, a};
                if (hasMono) {
                    move = pickGreedyBobMove(tc, color, a, b, true);
                } else if (tc.bip) {
                    move = bipBob.pick(a, b);
                } else {
                    move = pickGreedyBobMove(tc, color, a, b, false);
                }

                int v = move.first, c = move.second;
                if (v < 1 || v > tc.n || used[v]) {
                    
                    v = -1;
                    for (int x = 1; x <= tc.n; x++) if (!used[x]) { v = x; break; }
                    if (v == -1) {
                        sendMinusOne();
                        finish(_pe, "no available vertex to color");
                    }
                    c = a;
                }
                if (!(c == a || c == b)) c = a;

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

                applyColorAndCheck(v, c);
            }

            if (!hasMono) {
                
                sendMinusOne();
                finish(_wa, "Alice did not create monochromatic edge");
            }
        }
    }

    finish(_ok, "ok");
}


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

static long long edgeKey(int u, int v) {
    if (u > v) swap(u, v);
    return (static_cast<long long>(u) << 20) ^ static_cast<long long>(v);
}

struct GraphGen {
    int n = 0, m = 0;
    bool bip = true;

    unordered_set<long long> used;
    vector<pair<int,int>> edges;

    bool addEdge(int u, int v) {
        if (u == v) return false;
        if (u < 1 || u > n || v < 1 || v > n) return false;
        long long k = edgeKey(u, v);
        if (used.find(k) != used.end()) return false;
        used.insert(k);
        edges.push_back({u, v});
        return true;
    }

    int pickFromPart(const vector<int>& part, bool biased) {
        int sz = (int)part.size();
        if (sz == 0) return -1;
        if (!biased) return part[rnd.next(0, sz - 1)];
        int cap = min(sz, 64);
        int roll = rnd.next(1, 100);
        if (roll <= 85) return part[rnd.next(0, cap - 1)];
        return part[rnd.next(0, sz - 1)];
    }

    void build(bool wantBip) {
        bip = wantBip;
        used.reserve((size_t)m * 2 + 100);
        edges.reserve((size_t)m);

        if (n == 1) {
            m = 0;
            return;
        }

        
        for (int i = 2; i <= n && (int)edges.size() < m; i++) {
            addEdge(i - 1, i);
        }

        
        
        if (!bip) {
            if (n < 3) {
                bip = true;
            } else {
                if (m < n) m = n;
                
                if ((int)edges.size() < m) addEdge(1, 3);
            }
        }

        vector<int> part0, part1;
        part0.reserve((n + 1) / 2);
        part1.reserve((n + 1) / 2);
        for (int i = 1; i <= n; i++) {
            if (i & 1) part1.push_back(i);
            else part0.push_back(i);
        }

        auto wantCrossEdge = [&](void) -> bool {
            if (bip) return true;
            return rnd.next(1, 100) <= 90;
        };

        
        
        int attempts = 0;
        int maxAttempts = 2000000;
        while ((int)edges.size() < m && attempts < maxAttempts) {
            attempts++;

            bool cross = wantCrossEdge();
            if (cross) {
                bool biased = (rnd.next(1, 100) <= 80);
                bool uIn0 = (rnd.next(0, 1) == 0);
                int u = pickFromPart(uIn0 ? part0 : part1, biased);
                int v = pickFromPart(uIn0 ? part1 : part0, false);
                if (u == -1 || v == -1) continue;
                addEdge(u, v);
            } else {
                
                bool in0 = (rnd.next(0, 1) == 0);
                const vector<int>& part = in0 ? part0 : part1;
                if ((int)part.size() < 2) continue;
                int u = pickFromPart(part, true);
                int v = pickFromPart(part, true);
                if (u == -1 || v == -1 || u == v) continue;
                addEdge(u, v);
            }
        }

        
        if ((int)edges.size() < m) {
            for (int u = 1; u <= n && (int)edges.size() < m; u++) {
                for (int v = u + 1; v <= n && (int)edges.size() < m; v++) {
                    if (bip) {
                        if ( ((u ^ v) & 1) == 0 ) continue; 
                    }
                    addEdge(u, v);
                }
            }
        }

        
        if ((int)edges.size() > m) edges.resize(m);
    }
};

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

    string mode = opt<string>("mode", "non");
    long long baseSeed = 0;
    if (argc >= 2) baseSeed = atoll(argv[1]);

    
    int nOpt = opt<int>("n", 0);
    int mOpt = opt<int>("m", 0);
    int bipOpt = opt<int>("bip", -1); 

    auto decideDefaults = [&](int& n, int& m, bool& bip) {
        if (baseSeed == 1) {
            n = 1; m = 0; bip = true;
            return;
        }
        if (baseSeed == 2) {
            n = 2; m = 1; bip = true;
            return;
        }
        if (baseSeed == 3) {
            n = 3; m = 3; bip = false;
            return;
        }

        n = (nOpt > 0 ? nOpt : 10000);
        n = max(1, min(10000, n));

        int mMax = min(10000, n * (n - 1) / 2);
        if (mOpt > 0) m = mOpt;
        else m = mMax;

        m = max(0, min(mMax, m));
        if (n == 1) m = 0;
        else m = max(m, n - 1);

        if (bipOpt == 0) bip = false;
        else if (bipOpt == 1) bip = true;
        else bip = (baseSeed % 2 == 0); 
    };

    if (mode != "non" && mode != "adp") {
        mode = "non";
    }

    if (mode == "non") {
        int n, m;
        bool bip;
        decideDefaults(n, m, bip);

        GraphGen gg;
        gg.n = n;
        gg.m = m;

        
        if (!bip) {
            if (n < 3) bip = true;
            else if (m < n) m = n;
        }

        gg.m = m;
        gg.build(bip);

        cout << 1 << "\n";
        cout << gg.n << " " << gg.m << "\n";
        for (auto &e : gg.edges) {
            cout << e.first << " " << e.second << "\n";
        }
        return 0;
    }

    
    
    
    
    
    {
        int n, m;
        bool bip;
        decideDefaults(n, m, bip);

        
        if (!bip) {
            if (n < 3) bip = true;
            else if (m < n) m = n;
        }
        int mMax = min(10000, n * (n - 1) / 2);
        m = max(0, min(mMax, m));
        if (n == 1) m = 0;
        else m = max(m, n - 1);
        if (!bip && n >= 3) m = max(m, n);

        cout << 1 << "\n";
        cout << n << " " << m << " " << (bip ? 1 : 0) << "\n";
    }

    return 0;
}