Problem: cf1987_h
Difficulty: hard
Categories: search, math
Time limit: 4000 ms, memory limit: 512 MB

This temple only magnifies the mountain's power.

Badeline

This is an interactive problem.

You are given two positive integers $n$ and $m$ ($\bf{n \le m}$).

The jury has hidden from you a rectangular matrix $a$ with $n$ rows and $m$ columns, where $a_{i,j} \in \{ -1, 0, 1 \}$ for all $1 \le i \le n$ and $1 \le j \le m$. The jury has also selected a cell $(i_0, j_0)$. Your goal is to find $(i_0,j_0)$.

In one query, you give a cell $(i, j)$, then the jury will reply with an integer.

* If $(i, j) = (i_0, j_0)$, the jury will reply with $0$.
* Else, let $S$ be the sum of $a_{x,y}$ over all $x$ and $y$ such that $\min(i, i_0) \le x \le \max(i, i_0)$ and $\min(j, j_0) \le y \le \max(j, j_0)$. Then, the jury will reply with $|i - i_0| + |j - j_0| + |S|$.

Find $(i_0, j_0)$ by making at most $n + 225$ queries.

Note: the grader is not adaptive: $a$ and $(i_0,j_0)$ are fixed before any queries are made.

Input

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

The only line of each test case contains two integers $n$ and $m$ ($1 \le n \le m \le 5000$) — the numbers of rows and the number of columns of the hidden matrix $a$ respectively.

It is guaranteed that the sum of $n \cdot m$ over all test cases does not exceed $25 \cdot 10^6$.

Interaction

The interaction for each test case begins by reading the integers $n$ and $m$.

To make a query, output "? i j" ($1 \le i \le n, 1 \le j \le m$) without quotes. Afterwards, you should read one single integer — the answer to your query.

If you receive the integer $-1$ instead of an answer or a valid value of $n$ or $m$, it means your program has made an invalid query, has exceeded the limit of queries, or has given an incorrect answer on the previous test case. Your program must terminate immediately to receive a Wrong Answer verdict. Otherwise, you can get an arbitrary verdict because your solution will continue to read from a closed stream.

When you are ready to give the final answer, output "! i j" ($1 \le i \le n, 1 \le j \le m$) without quotes — the indices of the hidden cell. After solving a test case, your program should move to the next one immediately. After solving all test cases, your program should be terminated immediately.

After printing a query 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 4

5

3

5

1 1

0
```

Output

```
? 1 1

? 3 3

? 3 2

! 1 4

? 1 1

! 1 1
```

Note

The hidden matrix in the first test case:

|  |  |  |  |
| --- | --- | --- | --- |
| $1$ | $0$ | $1$ | $\color{red}{\textbf{0}}$ |
| $1$ | $0$ | $0$ | $1$ |
| $0$ | $-1$ | $-1$ | $-1$ |

The hidden matrix in the second test case:

|  |
| --- |
| $\color{red}{\textbf{0}}$ |

Note that the line breaks in the example input and output are for the sake of clarity, and do not occur in the real interaction.

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

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

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

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

struct TestCase {
    int n = 0, m = 0;
    int i0 = 0, j0 = 0;
    vector<int8_t> a; 
};

static bool readLongLong(long long& out) {
    if (!(cin >> out)) return false;
    return true;
}

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

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

    vector<TestCase> tests;
    int t = 0;

    try {
        t = inf.readInt();
        if (t < 1 || t > 50) finish(_pe, "Invalid t in hidden input");
        tests.resize(t);

        long long totalLimit = 0;

        for (int tc = 0; tc < t; tc++) {
            int n = inf.readInt();
            int m = inf.readInt();
            if (n < 1 || n > 5000 || m < 1 || m > 5000) finish(_pe, "Invalid n/m in hidden input");
            if (n > m) finish(_pe, "Hidden input violates n <= m");

            int i0 = inf.readInt();
            int j0 = inf.readInt();
            if (i0 < 1 || i0 > n || j0 < 1 || j0 > m) finish(_pe, "Invalid (i0,j0) in hidden input");

            TestCase& cs = tests[tc];
            cs.n = n; cs.m = m; cs.i0 = i0; cs.j0 = j0;
            cs.a.resize((size_t)n * (size_t)m);

            for (long long k = 0; k < 1LL * n * m; k++) {
                int v = inf.readInt();
                if (v != -1 && v != 0 && v != 1) finish(_pe, "Matrix value not in {-1,0,1}");
                cs.a[(size_t)k] = (int8_t)v;
            }

            totalLimit += (long long)n + 225;
        }

        query_limit = totalLimit;
    } catch (const exception& e) {
        
        finish(_pe, string("Exception while reading hidden input: ") + e.what());
    } catch (...) {
        finish(_pe, "Unknown exception while reading hidden input");
    }

    
    log_metrics();

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

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

    for (int tc = 0; tc < t; tc++) {
        const TestCase& cs = tests[tc];
        const int n = cs.n, m = cs.m;
        const int i0 = cs.i0, j0 = cs.j0;

        
        const int cols = m + 1;
        vector<int> pref((size_t)(n + 1) * (size_t)(m + 1), 0);

        for (int i = 1; i <= n; i++) {
            int rowSum = 0;
            const int prefRow = i * cols;
            const int prefPrev = (i - 1) * cols;
            const size_t aRow = (size_t)(i - 1) * (size_t)m;
            for (int j = 1; j <= m; j++) {
                rowSum += (int)cs.a[aRow + (size_t)(j - 1)];
                pref[prefRow + j] = pref[prefPrev + j] + rowSum;
            }
        }

        auto rectSum = [&](int r1, int c1, int r2, int c2) -> long long {
            
            if (r1 > r2) swap(r1, r2);
            if (c1 > c2) swap(c1, c2);
            long long A = pref[r2 * cols + c2];
            long long B = pref[(r1 - 1) * cols + c2];
            long long C = pref[r2 * cols + (c1 - 1)];
            long long D = pref[(r1 - 1) * cols + (c1 - 1)];
            return A - B - C + D;
        };

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

        while (true) {
            if (queries > hard_cap) finish(_pe, "Hard cap exceeded");

            string tok;
            if (!readToken(tok)) finish(_pe, "Unexpected EOF from contestant");

            if (tok == "?") {
                long long ii, jj;
                if (!readLongLong(ii) || !readLongLong(jj)) finish(_pe, "Malformed query arguments");
                if (ii < 1 || ii > n || jj < 1 || jj > m) finish(_pe, "Query indices out of range");

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

                long long ans = 0;
                if ((int)ii == i0 && (int)jj == j0) {
                    ans = 0;
                } else {
                    long long dist = llabs(ii - (long long)i0) + llabs(jj - (long long)j0);
                    long long S = rectSum((int)ii, (int)jj, i0, j0);
                    ans = dist + llabs(S);
                }

                cout << ans << "\n";
                cout.flush();
            } else if (tok == "!") {
                long long ii, jj;
                if (!readLongLong(ii) || !readLongLong(jj)) finish(_pe, "Malformed final answer arguments");
                if (ii < 1 || ii > n || jj < 1 || jj > m) finish(_pe, "Final answer indices out of range");
                if ((int)ii != i0 || (int)jj != j0) finish(_wa, "Wrong final answer");
                break; 
            } else {
                finish(_pe, "Unknown command token");
            }
        }
    }

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

static uint64_t fnv1a64(const string& s) {
    uint64_t h = 1469598103934665603ULL;
    for (unsigned char c : s) {
        h ^= (uint64_t)c;
        h *= 1099511628211ULL;
    }
    return h;
}

static uint64_t splitmix64(uint64_t& x) {
    uint64_t z = (x += 0x9e3779b97f4a7c15ULL);
    z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
    z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
    return z ^ (z >> 31);
}

struct MatrixParams {
    int n = 0, m = 0;
    int i0 = 0, j0 = 0;

    int pPos = 92; 
    int pNeg = 6;  
    uint32_t A = 1, B = 1, C = 0;

    int stripeW = 1;
    int stripePos = 1;
    int blockH = 1;
    int blockW = 1;
    int zeroRad = 0;
    bool allOnes = false;
};

static inline int clampi(int x, int lo, int hi) {
    if (x < lo) return lo;
    if (x > hi) return hi;
    return x;
}

static MatrixParams makeParams(int n, int m, int tc, uint64_t baseSeed) {
    MatrixParams p;
    p.n = n;
    p.m = m;

    uint64_t x = baseSeed;
    x ^= (uint64_t)n * 0x9e3779b97f4a7c15ULL;
    x ^= (uint64_t)m * 0xbf58476d1ce4e5b9ULL;
    x ^= (uint64_t)(tc + 1) * 0x94d049bb133111ebULL;

    p.i0 = 1 + (int)(splitmix64(x) % (uint64_t)n);
    p.j0 = 1 + (int)(splitmix64(x) % (uint64_t)m);

    
    p.allOnes = ((splitmix64(x) & 31ULL) == 0ULL);

    int baseRad = min(60, max(6, min(n, m) / 25));
    int radWiggle = (int)(splitmix64(x) % 11ULL) - 5; 
    p.zeroRad = clampi(baseRad + radWiggle, 0, n + m);

    p.pPos = 92;
    p.pNeg = 6;
    int posWiggle = (int)(splitmix64(x) % 7ULL) - 3; 
    p.pPos = clampi(p.pPos + posWiggle, 50, 98);
    int negWiggle = (int)(splitmix64(x) % 7ULL) - 3; 
    p.pNeg = clampi(p.pNeg + negWiggle, 1, 100 - p.pPos);

    p.A = 1 + (uint32_t)(splitmix64(x) % 1000000000ULL);
    p.B = 1 + (uint32_t)(splitmix64(x) % 1000000000ULL);
    p.C = (uint32_t)(splitmix64(x) % 1000000000ULL);

    int baseStripeW = max(1, m / 97);
    int stripeWiggle = (int)(splitmix64(x) % 9ULL) - 4; 
    p.stripeW = clampi(baseStripeW + stripeWiggle, 1, m);
    p.stripePos = 1 + (int)(splitmix64(x) % (uint64_t)(m - p.stripeW + 1));

    int baseBlockH = max(1, n / 3);
    int baseBlockW = max(1, m / 3);
    int bhWiggle = (int)(splitmix64(x) % 201ULL) - 100; 
    int bwWiggle = (int)(splitmix64(x) % 201ULL) - 100; 
    p.blockH = clampi(baseBlockH + bhWiggle, 1, n);
    p.blockW = clampi(baseBlockW + bwWiggle, 1, m);

    return p;
}

static inline int cellValue(const MatrixParams& p, int i, int j) {
    if (p.allOnes) return 1;

    bool inZeroDisk = (abs(i - p.i0) + abs(j - p.j0) <= p.zeroRad);
    if (inZeroDisk) return 0;

    bool inNegBlock = (i <= p.blockH && j <= p.blockW);
    bool inStripe = (j >= p.stripePos && j < p.stripePos + p.stripeW);

    int v = 1;
    if (inNegBlock || inStripe) {
        v = -1;
    } else {
        uint64_t x = (uint64_t)(uint32_t)i * (uint64_t)p.A + (uint64_t)(uint32_t)j * (uint64_t)p.B + (uint64_t)p.C;
        x ^= x >> 23;
        x *= 0x2127599bf4325c37ULL;
        x ^= x >> 47;
        int r = (int)(x % 100ULL);
        if (r < p.pPos) v = 1;
        else if (r < p.pPos + p.pNeg) v = -1;
        else v = 0;
        if (v == 0) v = 1; 
    }

    
    if (i == 1 || i == p.n || j == 1 || j == p.m) v = (v == -1 ? -1 : 1);
    
    if (((i ^ j ^ p.i0 ^ p.j0) & 31) == 0) v = -v;

    return v;
}

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

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

    
    
    int t = inf.readInt(1, 50, "t");

    vector<pair<int, int>> dims;
    dims.reserve(t);
    long long totalLimit = 0;
    for (int tc = 0; tc < t; tc++) {
        int n = inf.readInt(1, 5000, "n");
        int m = inf.readInt(1, 5000, "m");
        if (n > m) finish(_fail, "hidden input violates n <= m");
        dims.push_back({n, m});
        totalLimit += (long long)n + 225LL;
    }

    query_limit = totalLimit;
    log_metrics(); 

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

    const uint64_t baseSeed = fnv1a64(inf.name);

    cout << t << "\n" << flush;

    for (int tc = 0; tc < t; tc++) {
        const int n = dims[tc].first;
        const int m = dims[tc].second;

        const long long per_case_limit = (long long)n + 225LL;
        long long used = 0;

        MatrixParams params = makeParams(n, m, tc, baseSeed);

        
        const int cols = m + 1;
        vector<int> pref((size_t)(n + 1) * (size_t)(m + 1), 0);
        for (int i = 1; i <= n; i++) {
            int rowSum = 0;
            const size_t prefRow = (size_t)i * (size_t)cols;
            const size_t prefPrev = (size_t)(i - 1) * (size_t)cols;
            for (int j = 1; j <= m; j++) {
                rowSum += cellValue(params, i, j);
                pref[prefRow + (size_t)j] = pref[prefPrev + (size_t)j] + rowSum;
            }
        }

        auto rectSum = [&](int r1, int c1, int r2, int c2) -> long long {
            if (r1 > r2) swap(r1, r2);
            if (c1 > c2) swap(c1, c2);
            long long A = pref[(size_t)r2 * (size_t)cols + (size_t)c2];
            long long B = pref[(size_t)(r1 - 1) * (size_t)cols + (size_t)c2];
            long long C = pref[(size_t)r2 * (size_t)cols + (size_t)(c1 - 1)];
            long long D = pref[(size_t)(r1 - 1) * (size_t)cols + (size_t)(c1 - 1)];
            return A - B - C + D;
        };

        cout << n << " " << m << "\n" << flush;

        while (true) {
            if (queries > hard_cap) finish(_pe, "hard cap exceeded");

            string tok;
            if (!(cin >> tok)) finish(_pe, "unexpected EOF from contestant");

            if (tok == "?") {
                long long ii, jj;
                if (!(cin >> ii >> jj)) {
                    sendMinusOne();
                    finish(_pe, "malformed query arguments");
                }
                if (ii < 1 || ii > n || jj < 1 || jj > m) {
                    sendMinusOne();
                    finish(_wa, "query indices out of range");
                }

                queries++;
                used++;
                if (queries > hard_cap) finish(_pe, "hard cap exceeded");
                if (used > per_case_limit) {
                    sendMinusOne();
                    finish(_wa, "query limit exceeded");
                }

                long long ans = 0;
                if ((int)ii == params.i0 && (int)jj == params.j0) {
                    ans = 0;
                } else {
                    long long dist = llabs(ii - (long long)params.i0) + llabs(jj - (long long)params.j0);
                    long long S = rectSum((int)ii, (int)jj, params.i0, params.j0);
                    ans = dist + llabs(S);
                }

                cout << ans << "\n" << flush;
            } else if (tok == "!") {
                long long ii, jj;
                if (!(cin >> ii >> jj)) {
                    sendMinusOne();
                    finish(_pe, "malformed final answer arguments");
                }
                if (ii < 1 || ii > n || jj < 1 || jj > m) {
                    sendMinusOne();
                    finish(_wa, "final answer indices out of range");
                }
                if ((int)ii != params.i0 || (int)jj != params.j0) {
                    sendMinusOne();
                    finish(_wa, "wrong final answer");
                }
                break; 
            } else {
                sendMinusOne();
                finish(_pe, "unknown command token");
            }
        }
    }

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


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

static inline void appendVal(string& s, int v) {
    if (!s.empty()) s.push_back(' ');
    if (v == 1) {
        s.push_back('1');
    } else if (v == 0) {
        s.push_back('0');
    } else {
        s.push_back('-');
        s.push_back('1');
    }
}

struct Hasher {
    uint64_t A, B, C;
    int pPos, pNeg; 

    int base(int i, int j) const {
        uint64_t x = (uint64_t)(uint32_t)i * A + (uint64_t)(uint32_t)j * B + C;
        x ^= x >> 23;
        x *= 0x2127599bf4325c37ULL;
        x ^= x >> 47;
        int r = (int)(x % 100ULL);
        if (r < pPos) return 1;
        if (r < pPos + pNeg) return -1;
        return 0;
    }
};

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

    string mode = opt<string>("mode", "non");

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

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

    int n, m;
    if (nOpt != -1 || mOpt != -1) {
        n = (nOpt == -1 ? 5000 : nOpt);
        m = (mOpt == -1 ? 5000 : mOpt);
    } else {
        if (seed == 1) {
            n = 1; m = 1;
        } else if (seed == 2) {
            n = 1; m = 5000;
        } else if (seed == 3) {
            n = 5000; m = 5000;
        } else {
            n = 5000; m = 5000;
        }
    }

    if (n < 1) n = 1;
    if (m < 1) m = 1;
    if (n > 5000) n = 5000;
    if (m > 5000) m = 5000;
    if (n > m) swap(n, m);

    if (mode == "adp") {
        cout << 1 << "\n";
        cout << n << " " << m << "\n";
        return 0;
    }

    if (mode != "non") mode = "non";

    int i0Opt = opt<int>("i0", -1);
    int j0Opt = opt<int>("j0", -1);

    int i0, j0;
    if (i0Opt != -1 && j0Opt != -1) {
        i0 = i0Opt;
        j0 = j0Opt;
    } else {
        if (seed == 1) {
            i0 = 1; j0 = 1;
        } else if (seed == 2) {
            i0 = 1; j0 = (m + 1) / 2;
        } else if (seed == 3) {
            i0 = (n + 1) / 2; j0 = (m + 1) / 2;
        } else {
            i0 = rnd.next(1, n);
            j0 = rnd.next(1, m);
        }
    }
    if (i0 < 1) i0 = 1;
    if (i0 > n) i0 = n;
    if (j0 < 1) j0 = 1;
    if (j0 > m) j0 = m;

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

    if (seed == 1) {
        cout << 0 << "\n";
        return 0;
    }

    if (seed == 3) {
        string line;
        line.reserve((size_t)m * 2 + 16);
        for (int i = 1; i <= n; i++) {
            line.clear();
            for (int j = 1; j <= m; j++) appendVal(line, 1);
            cout << line << "\n";
        }
        return 0;
    }

    int pPos = opt<int>("pPos", (seed == 2 ? 50 : 92));
    int pNeg = opt<int>("pNeg", (seed == 2 ? 50 : 6));
    if (pPos < 0) pPos = 0;
    if (pNeg < 0) pNeg = 0;
    if (pPos > 100) pPos = 100;
    if (pNeg > 100 - pPos) pNeg = 100 - pPos;

    Hasher hasher;
    hasher.A = (uint64_t)(uint32_t)rnd.next(1, 1000000000);
    hasher.B = (uint64_t)(uint32_t)rnd.next(1, 1000000000);
    hasher.C = (uint64_t)(uint32_t)rnd.next(0, 1000000000);
    hasher.pPos = pPos;
    hasher.pNeg = pNeg;

    int stripeW = opt<int>("stripeW", max(1, m / 97));
    if (stripeW < 1) stripeW = 1;
    if (stripeW > m) stripeW = m;
    int stripePos = opt<int>("stripePos", rnd.next(1, m - stripeW + 1));

    int blockH = opt<int>("blockH", max(1, n / 3));
    int blockW = opt<int>("blockW", max(1, m / 3));
    if (blockH > n) blockH = n;
    if (blockW > m) blockW = m;

    int zeroRad = opt<int>("zeroRad", min(60, max(6, min(n, m) / 25)));
    if (zeroRad < 0) zeroRad = 0;

    string line;
    line.reserve((size_t)m * 3 + 16);

    for (int i = 1; i <= n; i++) {
        line.clear();
        for (int j = 1; j <= m; j++) {
            int v;

            
            bool inNegBlock = (i <= blockH && j <= blockW);
            bool inStripe = (j >= stripePos && j < stripePos + stripeW);
            bool inZeroDisk = (abs(i - i0) + abs(j - j0) <= zeroRad);

            if (seed == 2) {
                
                if (inZeroDisk && (j % 7 == 0)) v = 0;
                else v = ((j & 1) ? 1 : -1);
            } else {
                if (inZeroDisk) {
                    v = 0;
                } else if (inNegBlock) {
                    v = -1;
                } else if (inStripe) {
                    v = -1;
                } else {
                    v = hasher.base(i, j);
                    
                    if (v == 0) v = 1;
                }

                
                if (i == 1 || i == n || j == 1 || j == m) {
                    if (!inZeroDisk) v = (v == -1 ? -1 : 1);
                }

                
                if (!inZeroDisk && ((i ^ j ^ i0 ^ j0) & 31) == 0) v = -v;
            }

            appendVal(line, v);
        }
        cout << line << "\n";
    }

    return 0;
}