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

This is an interactive problem.

You are given a grid with $n$ rows and $m$ columns. You need to fill each cell with a unique integer from $1$ to $n \cdot m$.

After filling the grid, you will play a game on this grid against the interactor. Players take turns selecting one of the previously unselected cells from the grid, with the interactor going first.

On the first turn, the interactor can choose any cell from the grid. After that, any chosen cell must be orthogonally adjacent to at least one previously selected cell. Two cells are considered orthogonally adjacent if they share an edge. The game continues until all cells have been selected.

Your goal is to let the sum of numbers in the cells selected by you be strictly less than the sum of numbers in the cells selected by the interactor.

Input

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

The only line of each test case contains two integers $n$ and $m$ ($4 \le n, m \le 10$) — the number of rows and columns in the grid.

Interaction

First, output $n$ lines, each containing $m$ integers, representing the integers that you filled in the grid. Each integer from $1$ to $n \cdot m$ should appear exactly once.

Then, the game begins. The interactor and you take turns outputting coordinates to select a cell for $n \times m$ turns, with the interactor starting first.

On each player's (either you or the interactor) turn, the player will output two integers $i$ and $j$ ($1 \le i \le n$, $1 \le j \le m$), denoting that the player has selected the cell on the $i$-th row and $j$-th column. This cell should not have been selected in a previous round. Additionally, if it is not the first turn, the cell must be orthogonally adjacent to at least one previously selected cell.

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 \cdot m$ turns, if the sum of numbers in the cells selected by you is not strictly less than the sum of numbers in the cells selected by the interactor, the jury will output "-1" and you will receive a Wrong Answer verdict.

If your program has received a Wrong Answer verdict, it must terminate immediately. Otherwise, you may receive an arbitrary verdict because your solution might be reading from a closed stream.

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

```
1
4 4

3 4

4 4

4 2

4 1

1 4

1 2

2 2

2 1
```

Output

```
2 3 4 10
12 6 11 15
5 13 16 8
9 7 1 14

2 4

4 3

3 3

3 1

1 3

1 1

2 3

3 2
```

Note

Note that this is an example game and does not necessarily represent the optimal strategy for both players.

First, we fill a $4 \times 4$ grid with unique integers from $1$ to $16$ in the following way:

|  |  |  |  |
| --- | --- | --- | --- |
| $2$ | $3$ | $4$ | $10$ |
| $12$ | $6$ | $11$ | $15$ |
| $5$ | $13$ | $16$ | $8$ |
| $9$ | $7$ | $1$ | $14$ |

Next, the game begins.

1. The interactor first selects $(3, 4)$, which is the number $8$. For this selection, the interactor could choose any number. From the next selection onwards, each chosen number has to be adjacent to any previously selected number.
2. We select $(2, 4)$, which is the number $15$, adjacent to $(3, 4)$.
3. The interactor selects $(4, 4)$, which is the number $14$, adjacent to $(3, 4)$.
4. We select $(4, 3)$, which is the number $1$, adjacent to $(4, 4)$.
5. $\ldots$
6. This is continued until all numbers are selected.

In the end, the numbers we selected were $[15, 1, 16, 5, 4, 2, 11, 13]$, and the numbers selected by the interactor were $[8, 14, 7, 9, 10, 3, 6, 12]$. The sum of the numbers we selected is $67$, which is less than the sum of the numbers selected by the interactor $69$. Therefore, we have won this game.

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 parseLongLongStrict(const string& s, long long& out) {
    if (s.empty()) return false;
    int i = 0;
    if (s[0] == '-') {
        i = 1;
        if ((int)s.size() == 1) return false;
    }
    for (; i < (int)s.size(); i++) if (!isdigit((unsigned char)s[i])) return false;
    try {
        size_t idx = 0;
        long long v = stoll(s, &idx, 10);
        if (idx != s.size()) return false;
        out = v;
        return true;
    } catch (...) {
        return false;
    }
}

static long long readLLFromCinOrFinish(const string& what) {
    string tok;
    if (!(cin >> tok)) {
        cout << -1 << endl;
        finish(_pe, "Unexpected EOF while reading " + what);
    }
    long long x;
    if (!parseLongLongStrict(tok, x)) {
        cout << -1 << endl;
        finish(_pe, "Malformed integer while reading " + what);
    }
    return x;
}

struct TestCase {
    int n = 0, m = 0;
    vector<pair<int,int>> path; 
};

static bool adjacent4(int r1, int c1, int r2, int c2) {
    return (abs(r1 - r2) + abs(c1 - c2)) == 1;
}

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

    
    int t = inf.readInt(1, 100, "t");
    vector<TestCase> tests;
    tests.reserve(t);

    long long totalContestantMoves = 0;
    for (int tc = 0; tc < t; tc++) {
        TestCase cs;
        cs.n = inf.readInt(4, 10, "n");
        cs.m = inf.readInt(4, 10, "m");
        int N = cs.n * cs.m;
        cs.path.reserve(N);
        vector<vector<int>> seen(cs.n, vector<int>(cs.m, 0));
        for (int k = 0; k < N; k++) {
            int r = inf.readInt(1, cs.n, "path_r") - 1;
            int c = inf.readInt(1, cs.m, "path_c") - 1;
            if (seen[r][c]) finish(_pe, "Hidden path has duplicates");
            seen[r][c] = 1;
            cs.path.push_back({r, c});
            if (k > 0) {
                auto [pr, pc] = cs.path[k - 1];
                if (!adjacent4(pr, pc, r, c)) finish(_pe, "Hidden path is not consecutive-adjacent");
            }
        }
        tests.push_back(std::move(cs));
        totalContestantMoves += (long long)(tests.back().n * tests.back().m) / 2;
    }

    query_limit = totalContestantMoves;
    log_metrics(); 

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

    
    cout << t << endl;

    for (int tc = 0; tc < t; tc++) {
        const int n = tests[tc].n, m = tests[tc].m;
        const int N = n * m;
        const auto& path = tests[tc].path;

        cout << n << " " << m << endl;

        
        vector<vector<int>> a(n, vector<int>(m, 0));
        vector<char> used(N + 1, 0);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                long long vll = readLLFromCinOrFinish("grid value");
                if (vll < 1 || vll > N) {
                    cout << -1 << endl;
                    finish(_wa, "Grid value out of range");
                }
                int v = (int)vll;
                if (used[v]) {
                    cout << -1 << endl;
                    finish(_wa, "Grid value duplicated");
                }
                used[v] = 1;
                a[i][j] = v;
            }
        }

        
        vector<vector<char>> selected(n, vector<char>(m, 0));
        long long sumInter = 0, sumCont = 0;
        int p = 0;

        auto pickInteractor = [&]() -> pair<int,int> {
            while (p < N && selected[path[p].first][path[p].second]) p++;
            if (p >= N) finish(_pe, "No available move for interactor (should not happen)");
            return path[p];
        };

        auto isAdjacentToSelected = [&](int r, int c) -> bool {
            static const int dr[4] = {1, -1, 0, 0};
            static const int dc[4] = {0, 0, 1, -1};
            for (int k = 0; k < 4; k++) {
                int nr = r + dr[k], nc = c + dc[k];
                if (0 <= nr && nr < n && 0 <= nc && nc < m && selected[nr][nc]) return true;
            }
            return false;
        };

        for (int turn = 0; turn < N; turn++) {
            if (turn % 2 == 0) {
                
                auto [r, c] = pickInteractor();
                if (turn > 0 && !isAdjacentToSelected(r, c)) finish(_pe, "Interactor move not adjacent (hidden path invariant broken)");
                selected[r][c] = 1;
                sumInter += a[r][c];
                cout << (r + 1) << " " << (c + 1) << endl;
            } else {
                
                long long iLL = readLLFromCinOrFinish("contestant row");
                long long jLL = readLLFromCinOrFinish("contestant col");
                queries++;
                if (queries > hard_cap) {
                    cout << -1 << endl;
                    finish(_pe, "Hard cap exceeded");
                }

                if (iLL == -1 || jLL == -1) {
                    cout << -1 << endl;
                    finish(_wa, "Contestant terminated with -1");
                }

                if (iLL < 1 || iLL > n || jLL < 1 || jLL > m) {
                    cout << -1 << endl;
                    finish(_wa, "Move out of bounds");
                }
                int r = (int)iLL - 1, c = (int)jLL - 1;

                if (selected[r][c]) {
                    cout << -1 << endl;
                    finish(_wa, "Cell already selected");
                }
                if (!isAdjacentToSelected(r, c)) {
                    cout << -1 << endl;
                    finish(_wa, "Move not adjacent to selected set");
                }

                selected[r][c] = 1;
                sumCont += a[r][c];
            }
        }

        if (!(sumCont < sumInter)) {
            cout << -1 << endl;
            finish(_wa, "Final sum condition failed");
        }
    }

    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 readIntFromContestant(long long& x) {
    cin >> x;
    return (bool)cin;
}

struct MoveEval {
    long long delta;
    int minOpp;
    int frontierSize;
    int ourVal;
    int r, c;
};

static bool betterForInteractor(const MoveEval& a, const MoveEval& b) {
    if (a.delta != b.delta) return a.delta < b.delta;                 
    if (a.minOpp != b.minOpp) return a.minOpp > b.minOpp;             
    if (a.frontierSize != b.frontierSize) return a.frontierSize < b.frontierSize; 
    if (a.ourVal != b.ourVal) return a.ourVal < b.ourVal;             
    if (a.r != b.r) return a.r < b.r;
    return a.c < b.c;
}

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

    int t = inf.readInt(1, 100, "t");
    vector<pair<int,int>> tests;
    tests.reserve(t);

    long long totalContestantMoves = 0;
    for (int tc = 0; tc < t; tc++) {
        int n = inf.readInt(4, 10, "n");
        int m = inf.readInt(4, 10, "m");
        tests.push_back({n, m});
        totalContestantMoves += (1LL * n * m) / 2; 
    }

    query_limit = totalContestantMoves;
    log_metrics(); 

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

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

    for (int tc = 0; tc < t; tc++) {
        int n = tests[tc].first;
        int m = tests[tc].second;
        int nm = n * m;

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

        vector<vector<int>> val(n, vector<int>(m, 0));
        vector<char> seen(nm + 1, 0);

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                long long xll;
                if (!readIntFromContestant(xll)) {
                    cout << -1 << "\n";
                    cout.flush();
                    finish(_pe, "EOF / non-integer while reading grid");
                }
                if (xll < 1 || xll > nm) {
                    cout << -1 << "\n";
                    cout.flush();
                    finish(_wa, "grid value out of range");
                }
                int x = (int)xll;
                if (seen[x]) {
                    cout << -1 << "\n";
                    cout.flush();
                    finish(_wa, "duplicate value in grid");
                }
                seen[x] = 1;
                val[i][j] = x;
            }
        }

        vector<vector<char>> picked(n, vector<char>(m, 0));
        int pickedCount = 0;
        long long sumInteractor = 0, sumContestant = 0;

        auto adjPicked = [&](int r, int c) -> bool {
            static const int dr[4] = {1, -1, 0, 0};
            static const int dc[4] = {0, 0, 1, -1};
            for (int k = 0; k < 4; k++) {
                int nr = r + dr[k], nc = c + dc[k];
                if (0 <= nr && nr < n && 0 <= nc && nc < m && picked[nr][nc]) return true;
            }
            return false;
        };

        auto chooseInteractorMove = [&]() -> pair<int,int> {
            MoveEval best;
            bool have = false;

            for (int r = 0; r < n; r++) {
                for (int c = 0; c < m; c++) {
                    if (picked[r][c]) continue;
                    if (pickedCount != 0 && !adjPicked(r, c)) continue;

                    int ourV = val[r][c];

                    picked[r][c] = 1;
                    int minOpp = INT_MAX;
                    int frontierSize = 0;

                    if (pickedCount + 1 < nm) {
                        for (int rr = 0; rr < n; rr++) {
                            for (int cc = 0; cc < m; cc++) {
                                if (picked[rr][cc]) continue;
                                if (!adjPicked(rr, cc)) continue;
                                frontierSize++;
                                minOpp = min(minOpp, val[rr][cc]);
                            }
                        }
                        if (minOpp == INT_MAX) {
                            
                            minOpp = 0;
                            frontierSize = 0;
                        }
                    } else {
                        minOpp = 0;
                        frontierSize = 0;
                    }

                    picked[r][c] = 0;

                    MoveEval cur;
                    cur.delta = 1LL * ourV - 1LL * minOpp;
                    cur.minOpp = minOpp;
                    cur.frontierSize = frontierSize;
                    cur.ourVal = ourV;
                    cur.r = r;
                    cur.c = c;

                    if (!have || betterForInteractor(cur, best)) {
                        best = cur;
                        have = true;
                    }
                }
            }

            if (!have) finish(_fail, "interactor has no legal move (internal error)");
            return {best.r, best.c};
        };

        for (int turn = 0; turn < nm; turn++) {
            if (turn % 2 == 0) {
                auto [r, c] = chooseInteractorMove();
                picked[r][c] = 1;
                pickedCount++;
                sumInteractor += val[r][c];

                cout << (r + 1) << " " << (c + 1) << "\n";
                cout.flush();
            } else {
                long long iLL, jLL;
                if (!readIntFromContestant(iLL) || !readIntFromContestant(jLL)) {
                    cout << -1 << "\n";
                    cout.flush();
                    finish(_pe, "EOF / non-integer on contestant move");
                }
                queries++;
                if (queries > hard_cap) {
                    cout << -1 << "\n";
                    cout.flush();
                    finish(_pe, "too many queries (hard cap exceeded)");
                }

                if (iLL < 1 || iLL > n || jLL < 1 || jLL > m) {
                    cout << -1 << "\n";
                    cout.flush();
                    finish(_wa, "contestant move out of bounds");
                }
                int r = (int)iLL - 1;
                int c = (int)jLL - 1;

                if (picked[r][c]) {
                    cout << -1 << "\n";
                    cout.flush();
                    finish(_wa, "contestant picked an already selected cell");
                }
                if (!adjPicked(r, c)) {
                    cout << -1 << "\n";
                    cout.flush();
                    finish(_wa, "contestant move not adjacent to selected region");
                }

                picked[r][c] = 1;
                pickedCount++;
                sumContestant += val[r][c];
            }
        }

        if (pickedCount != nm) finish(_fail, "pickedCount mismatch (internal error)");
        if (!(sumContestant < sumInteractor)) {
            cout << -1 << "\n";
            cout.flush();
            finish(_wa, "final sum condition failed");
        }
    }

    finish(_ok, "OK");
}


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

static vector<pair<int,int>> makeSnakeRows(int n, int m) {
    vector<pair<int,int>> path;
    path.reserve(n * m);
    for (int r = 0; r < n; r++) {
        if (r % 2 == 0) {
            for (int c = 0; c < m; c++) path.push_back({r, c});
        } else {
            for (int c = m - 1; c >= 0; c--) path.push_back({r, c});
        }
    }
    return path;
}

static vector<pair<int,int>> makeSnakeCols(int n, int m) {
    vector<pair<int,int>> path;
    path.reserve(n * m);
    for (int c = 0; c < m; c++) {
        if (c % 2 == 0) {
            for (int r = 0; r < n; r++) path.push_back({r, c});
        } else {
            for (int r = n - 1; r >= 0; r--) path.push_back({r, c});
        }
    }
    return path;
}

static vector<pair<int,int>> makeSpiral(int n, int m, int sr, int sc, int dir0) {
    
    
    static const int dr[4] = {0, 1, 0, -1};
    static const int dc[4] = {1, 0, -1, 0};

    vector<vector<char>> vis(n, vector<char>(m, 0));
    vector<pair<int,int>> path;
    path.reserve(n * m);

    int r = sr, c = sc, dir = dir0;
    for (int step = 0; step < n * m; step++) {
        vis[r][c] = 1;
        path.push_back({r, c});
        if (step + 1 == n * m) break;

        for (int tries = 0; tries < 4; tries++) {
            int nr = r + dr[dir], nc = c + dc[dir];
            if (0 <= nr && nr < n && 0 <= nc && nc < m && !vis[nr][nc]) {
                r = nr; c = nc;
                break;
            }
            dir = (dir + 1) & 3;
        }
    }
    return path;
}

static bool isAdjacent(const pair<int,int>& a, const pair<int,int>& b) {
    return (abs(a.first - b.first) + abs(a.second - b.second)) == 1;
}

static void ensureHamiltonianAdjacency(const vector<pair<int,int>>& path, int n, int m) {
    if ((int)path.size() != n * m) quitf(_fail, "Path size mismatch");
    vector<vector<int>> seen(n, vector<int>(m, 0));
    for (auto [r, c] : path) {
        if (r < 0 || r >= n || c < 0 || c >= m) quitf(_fail, "Out of bounds in path");
        if (seen[r][c]) quitf(_fail, "Duplicate cell in path");
        seen[r][c] = 1;
    }
    for (int i = 1; i < (int)path.size(); i++) {
        if (!isAdjacent(path[i - 1], path[i])) quitf(_fail, "Non-adjacent consecutive cells");
    }
}

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

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

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

    int n, m;

    if (nOpt != -1 && mOpt != -1) {
        n = nOpt;
        m = mOpt;
    } else if (seed == 1) {
        n = 4; m = 4;          
    } else if (seed == 2) {
        n = 4; m = 10;         
    } else if (seed == 3) {
        n = 10; m = 4;         
    } else {
        
        int pick = rnd.next(0, 11);
        vector<pair<int,int>> candidates = {
            {10, 10}, {10, 9}, {9, 10}, {9, 9},
            {10, 8}, {8, 10}, {10, 7}, {7, 10},
            {8, 9}, {9, 8}, {10, 6}, {6, 10}
        };
        n = candidates[pick].first;
        m = candidates[pick].second;
        if (rnd.next(0, 3) == 0) swap(n, m);
    }

    n = max(4, min(10, n));
    m = max(4, min(10, m));

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

    if (mode == "non") {
        
        
        vector<pair<int,int>> path;

        if (seed <= 3) {
            
            
            int corner = (seed - 1) % 4;
            int sr = (corner >= 2) ? (n - 1) : 0;
            int sc = (corner % 2) ? (m - 1) : 0;
            
            int dir0;
            if (sr == 0 && sc == 0) dir0 = 0;           
            else if (sr == 0 && sc == m - 1) dir0 = 1;  
            else if (sr == n - 1 && sc == m - 1) dir0 = 2; 
            else dir0 = 3;                               
            path = makeSpiral(n, m, sr, sc, dir0);
        } else {
            int type = rnd.next(0, 2); 
            if (type == 0) {
                path = makeSnakeRows(n, m);
                if (rnd.next(0, 1) == 1) reverse(path.begin(), path.end());
            } else if (type == 1) {
                path = makeSnakeCols(n, m);
                if (rnd.next(0, 1) == 1) reverse(path.begin(), path.end());
            } else {
                int corner = rnd.next(0, 3);
                int sr = (corner >= 2) ? (n - 1) : 0;
                int sc = (corner % 2) ? (m - 1) : 0;
                int dir0;
                if (sr == 0 && sc == 0) dir0 = 0;
                else if (sr == 0 && sc == m - 1) dir0 = 1;
                else if (sr == n - 1 && sc == m - 1) dir0 = 2;
                else dir0 = 3;
                
                dir0 = (dir0 + rnd.next(0, 1)) & 3;
                path = makeSpiral(n, m, sr, sc, dir0);
                if (rnd.next(0, 2) == 0) reverse(path.begin(), path.end());
            }
        }

        ensureHamiltonianAdjacency(path, n, m);

        for (auto [r, c] : path) {
            cout << (r + 1) << " " << (c + 1) << "\n";
        }
    } else if (mode == "adp") {
        
        
    } else {
        
        
    }

    return 0;
}