Problem: cf1493_f
Difficulty: medium
Categories: search, math
Time limit: 1000 ms, memory limit: 256 MB

This is an interactive problem.

There exists a matrix $a$ of size $n \times m$ ($n$ rows and $m$ columns), you know only numbers $n$ and $m$. The rows of the matrix are numbered from $1$ to $n$ from top to bottom, and columns of the matrix are numbered from $1$ to $m$ from left to right. The cell on the intersection of the $x$-th row and the $y$-th column is denoted as $(x, y)$.

You are asked to find the number of pairs $(r, c)$ ($1 \le r \le n$, $1 \le c \le m$, $r$ is a divisor of $n$, $c$ is a divisor of $m$) such that if we split the matrix into rectangles of size $r \times c$ (of height $r$ rows and of width $c$ columns, each cell belongs to exactly one rectangle), all those rectangles are pairwise equal.

You can use queries of the following type:

* ? $h$ $w$ $i_1$ $j_1$ $i_2$ $j_2$ ($1 \le h \le n$, $1 \le w \le m$, $1 \le i_1, i_2 \le n$, $1 \le j_1, j_2 \le m$) — to check if non-overlapping subrectangles of height $h$ rows and of width $w$ columns of matrix $a$ are equal or not. The upper left corner of the first rectangle is $(i_1, j_1)$. The upper left corner of the second rectangle is $(i_2, j_2)$. Subrectangles overlap, if they have at least one mutual cell. If the subrectangles in your query have incorrect coordinates (for example, they go beyond the boundaries of the matrix) or overlap, your solution will be considered incorrect.

You can use at most $ 3 \cdot \left \lfloor{ \log_2{(n+m)} } \right \rfloor$ queries. All elements of the matrix $a$ are fixed before the start of your program and do not depend on your queries.

Input

The first line contains two integers $n$ and $m$ ($1 \le n, m \le 1000$) — the number of rows and columns, respectively.

Output

When ready, print a line with an exclamation mark ('!') and then the answer — the number of suitable pairs $(r, c)$. After that your program should terminate.

Interaction

To make a query, print a line with the format "? $h$ $w$ $i_1$ $j_1$ $i_2$ $j_2$ ", where the integers are the height and width and the coordinates of upper left corners of non-overlapping rectangles, about which you want to know if they are equal or not.

After each query read a single integer $t$ ($t$ is $0$ or $1$): if the subrectangles are equal, $t=1$, otherwise $t=0$.

In case your query is of incorrect format or you asked more than $3 \cdot \left \lfloor{ \log_2{(n+m)} } \right \rfloor$ queries, you will receive the Wrong Answer verdict.

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.

It is guaranteed that the matrix $a$ is fixed and won't change during the interaction process.

Example

Input

```
3 4
1
1
1
0
```

Output

```
? 1 2 1 1 1 3
? 1 2 2 1 2 3
? 1 2 3 1 3 3
? 1 1 1 1 1 2
! 2
```

Note

In the example test the matrix $a$ of size $3 \times 4$ is equal to:

```
1 2 1 2  
3 3 3 3  
2 1 2 1
```

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;


void log_metrics() {
    try {
        tout << "queries=" << queries << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {}
}


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


struct Hasher {
    long long MOD;
    long long B1, B2;
    vector<long long> pow1, pow2;
    vector<vector<long long>> pref;
    int N, M;

    Hasher(int n, int m, long long mod, long long b1, long long b2, const vector<vector<int>>& mat) 
        : N(n), M(m), MOD(mod), B1(b1), B2(b2) {
        
        
        pow1.resize(N + 1);
        pow2.resize(M + 1);
        pow1[0] = 1; 
        for(int i=1; i<=N; ++i) pow1[i] = (pow1[i-1] * B1) % MOD;
        pow2[0] = 1; 
        for(int i=1; i<=M; ++i) pow2[i] = (pow2[i-1] * B2) % MOD;

        
        pref.assign(N + 1, vector<long long>(M + 1, 0));
        for(int i=1; i<=N; ++i) {
            for(int j=1; j<=M; ++j) {
                long long val = mat[i-1][j-1]; 
                val %= MOD;
                long long term = (val * pow1[i]) % MOD;
                term = (term * pow2[j]) % MOD;
                
                long long res = (pref[i-1][j] + pref[i][j-1]) % MOD;
                res = (res - pref[i-1][j-1] + MOD) % MOD;
                res = (res + term) % MOD;
                pref[i][j] = res;
            }
        }
    }

    
    long long get(int r, int c, int h, int w) {
        int r2 = r + h - 1;
        int c2 = c + w - 1;
        long long res = (pref[r2][c2] - pref[r-1][c2] + MOD) % MOD;
        res = (res - pref[r2][c-1] + MOD) % MOD;
        res = (res + pref[r-1][c-1]) % MOD;
        return res;
    }

    
    bool equal(int r1, int c1, int r2, int c2, int h, int w) {
        long long h1 = get(r1, c1, h, w);
        long long h2 = get(r2, c2, h, w);
        
        
        
        long long left = (h1 * pow1[r2]) % MOD;
        left = (left * pow2[c2]) % MOD;
        
        long long right = (h2 * pow1[r1]) % MOD;
        right = (right * pow2[c1]) % MOD;
        
        return left == right;
    }
};

vector<vector<int>> mat;
int N, M;


bool overlap(int r1, int c1, int r2, int c2, int h, int w) {
    int r1_end = r1 + h; 
    int r2_end = r2 + h;
    int c1_end = c1 + w;
    int c2_end = c2 + w;
    
    int r_start = max(r1, r2);
    int r_end = min(r1_end, r2_end);
    int c_start = max(c1, c2);
    int c_end = min(c1_end, c2_end);
    
    return (r_start < r_end && c_start < c_end);
}

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

    
    N = inf.readInt();
    M = inf.readInt();
    mat.assign(N, vector<int>(M));
    for(int i=0; i<N; ++i) {
        for(int j=0; j<M; ++j) {
            mat[i][j] = inf.readInt();
        }
    }
    
    
    Hasher H1(N, M, 1000000007, 1009, 1013, mat);
    Hasher H2(N, M, 1000000009, 1019, 1021, mat);

    
    
    int v_cnt = 0;
    for (int r = 1; r <= N; ++r) {
        if (N % r == 0) {
            if (r == N) {
                v_cnt++;
            } else {
                
                if (H1.equal(1, 1, 1+r, 1, N-r, M) && H2.equal(1, 1, 1+r, 1, N-r, M)) {
                    v_cnt++;
                }
            }
        }
    }
    
    
    int h_cnt = 0;
    for (int c = 1; c <= M; ++c) {
        if (M % c == 0) {
            if (c == M) {
                h_cnt++;
            } else {
                
                if (H1.equal(1, 1, 1, 1+c, N, M-c) && H2.equal(1, 1, 1, 1+c, N, M-c)) {
                    h_cnt++;
                }
            }
        }
    }
    
    long long ans = (long long)v_cnt * h_cnt;
    
    
    query_limit = 3LL * (long long)floor(log2(N + M));
    
    
    cout << N << " " << M << endl;
    log_metrics(); 

    while (true) {
        string token;
        if (!(cin >> token)) {
             finish(_pe, "Unexpected EOF (expected '?' or '!')");
        }
        
        if (token == "!") {
             long long user_ans;
             if (!(cin >> user_ans)) finish(_pe, "Expected answer after '!'");
             if (user_ans == ans) {
                 finish(_ok, "Correct answer");
             } else {
                 finish(_wa, "Wrong answer. Expected " + to_string(ans) + ", found " + to_string(user_ans));
             }
        } else {
             int h, w, i1, j1, i2, j2;
             
             if (token == "?") {
                 if (!(cin >> h >> w >> i1 >> j1 >> i2 >> j2)) finish(_pe, "Invalid query format");
             } else {
                 try {
                     h = stoi(token);
                 } catch (...) {
                     finish(_pe, "Invalid token (expected integer or '?'/'!')");
                 }
                 if (!(cin >> w >> i1 >> j1 >> i2 >> j2)) finish(_pe, "Invalid query format");
             }
             
             queries++;
             if (queries > 200000) finish(_pe, "Hard query limit exceeded");

             
             if (h < 1 || h > N || w < 1 || w > M) finish(_wa, "Dimensions out of bounds");
             if (i1 < 1 || i1 > N || j1 < 1 || j1 > M) finish(_wa, "Coords 1 out of bounds");
             if (i2 < 1 || i2 > N || j2 < 1 || j2 > M) finish(_wa, "Coords 2 out of bounds");
             
             if (i1 + h - 1 > N || j1 + w - 1 > M) finish(_wa, "Rect 1 out of bounds");
             if (i2 + h - 1 > N || j2 + w - 1 > M) finish(_wa, "Rect 2 out of bounds");
             
             if (overlap(i1, j1, i2, j2, h, w)) finish(_wa, "Rectangles overlap");
             
             
             bool eq = H1.equal(i1, j1, i2, j2, h, w) && H2.equal(i1, j1, i2, j2, h, w);
             cout << (eq ? 1 : 0) << endl;
             
             
             if (queries % 10 == 0) log_metrics();
        }
    }
}


Adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;


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


void log_metrics() {
    if (query_limit == 0) return; 
    try {
        tout << "queries=" << queries << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {
        
    }
}


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


int count_divisors(int x) {
    int cnt = 0;
    for (int i = 1; i * i <= x; i++) {
        if (x % i == 0) {
            cnt++;
            if (i * i != x) cnt++;
        }
    }
    return cnt;
}

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

    
    int n = inf.readInt();
    int m = inf.readInt();

    
    
    query_limit = 3LL * (long long)floor(log2(n + m));

    
    vector<long long> rest;
    while (!inf.seekEof()) {
        rest.push_back(inf.readLong());
    }

    int R = -1, C = -1;
    vector<vector<int>> mat;

    
    
    
    
    
    
    if (rest.size() == (size_t)n * m) {
        
        mat.assign(n, vector<int>(m));
        int idx = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                mat[i][j] = (int)rest[idx++];
            }
        }

        
        R = n;
        vector<int> divs_n;
        for (int i = 1; i * i <= n; i++) {
            if (n % i == 0) {
                divs_n.push_back(i);
                if (i * i != n) divs_n.push_back(n / i);
            }
        }
        sort(divs_n.begin(), divs_n.end());
        
        for (int r : divs_n) {
            bool ok = true;
            
            for (int i = 0; i < n - r; i++) {
                for (int j = 0; j < m; j++) {
                    if (mat[i][j] != mat[i + r][j]) {
                        ok = false; break;
                    }
                }
                if (!ok) break;
            }
            if (ok) { R = r; break; }
        }

        
        C = m;
        vector<int> divs_m;
        for (int i = 1; i * i <= m; i++) {
            if (m % i == 0) {
                divs_m.push_back(i);
                if (i * i != m) divs_m.push_back(m / i);
            }
        }
        sort(divs_m.begin(), divs_m.end());

        for (int c : divs_m) {
            bool ok = true;
            
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m - c; j++) {
                    if (mat[i][j] != mat[i][j + c]) {
                        ok = false; break;
                    }
                }
                if (!ok) break;
            }
            if (ok) { C = c; break; }
        }

    } else if (rest.size() == 2) {
        
        R = (int)rest[0];
        C = (int)rest[1];
    } else {
        finish(_fail, "Invalid input format in hidden file (token count mismatch)");
    }

    
    
    long long correct_ans = (long long)count_divisors(n / R) * count_divisors(m / C);

    
    atexit(log_metrics);
    log_metrics();

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

    
    while (true) {
        string type;
        if (!(cin >> type)) {
            
            finish(_wa, "Unexpected EOF (no answer provided)");
        }

        if (type == "!") {
            long long user_ans;
            if (!(cin >> user_ans)) {
                finish(_wa, "Expected integer answer after '!'");
            }
            if (user_ans == correct_ans) {
                finish(_ok, "Correct");
            } else {
                finish(_wa, "Wrong answer: expected " + to_string(correct_ans) + ", found " + to_string(user_ans));
            }
        } else if (type == "?") {
            queries++;
            
            if (queries % 10 == 0) log_metrics();

            
            if (queries > max(200000LL, 10 * query_limit)) {
                finish(_pe, "Hard query limit exceeded");
            }

            int h, w, i1, j1, i2, j2;
            if (!(cin >> h >> w >> i1 >> j1 >> i2 >> j2)) {
                finish(_wa, "Invalid query format");
            }

            
            if (h < 1 || h > n || w < 1 || w > m) finish(_wa, "Invalid rectangle size");
            
            if (i1 < 1 || i1 > n - h + 1 || j1 < 1 || j1 > m - w + 1) finish(_wa, "Rectangle 1 out of bounds");
            if (i2 < 1 || i2 > n - h + 1 || j2 < 1 || j2 > m - w + 1) finish(_wa, "Rectangle 2 out of bounds");

            
            
            
            int r_start = max(i1, i2);
            int r_end = min(i1 + h - 1, i2 + h - 1);
            int c_start = max(j1, j2);
            int c_end = min(j1 + w - 1, j2 + w - 1);

            if (r_start <= r_end && c_start <= c_end) {
                finish(_wa, "Rectangles overlap");
            }

            
            bool eq = false;
            if (!mat.empty()) {
                
                eq = true;
                for (int r_off = 0; r_off < h; r_off++) {
                    for (int c_off = 0; c_off < w; c_off++) {
                        if (mat[i1 + r_off - 1][j1 + c_off - 1] != mat[i2 + r_off - 1][j2 + c_off - 1]) {
                            eq = false;
                            break;
                        }
                    }
                    if (!eq) break;
                }
            } else {
                
                
                if (abs(i1 - i2) % R == 0 && abs(j1 - j2) % C == 0) {
                    eq = true;
                }
            }

            cout << (eq ? 1 : 0) << endl;
        } else {
            finish(_wa, "Unknown token '" + type + "' (expected '?' or '!')");
        }
    }

    return 0;
}


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

using namespace std;


vector<int> get_divisors(int n) {
    vector<int> divs;
    for (int i = 1; i * i <= n; i++) {
        if (n % i == 0) {
            divs.push_back(i);
            if (i * i != n) divs.push_back(n / i);
        }
    }
    return divs;
}

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);
    
    
    string mode = opt<string>("mode", "non");

    
    int n, m, r, c;
    long long seed_val = atoll(argv[1]);

    if (seed_val == 1) {
        
        n = 1000; m = 1000;
        r = n; c = m;
    } else if (seed_val == 2) {
        
        n = 1000; m = 1000;
        r = 1; c = 1;
    } else if (seed_val == 3) {
        
        n = 997; m = 991;
        r = n; c = m;
    } else if (seed_val == 4) {
        
        n = 10; m = 10;
        r = 10; c = 10;
    } else if (seed_val <= 10) {
        
        int hcn[] = {12, 24, 36, 48, 60, 120, 240, 360, 720, 840};
        int sz = sizeof(hcn)/sizeof(hcn[0]);
        n = hcn[rnd.next(sz)];
        m = hcn[rnd.next(sz)];
        vector<int> dn = get_divisors(n);
        vector<int> dm = get_divisors(m);
        r = dn[rnd.next(dn.size())];
        c = dm[rnd.next(dm.size())];
    } else {
        
        n = rnd.next(1, 1000);
        m = rnd.next(1, 1000);
        
        
        vector<int> dn = get_divisors(n);
        vector<int> dm = get_divisors(m);
        r = dn[rnd.next(dn.size())];
        c = dm[rnd.next(dm.size())];
    }

    if (mode == "adp") {
        
        
        cout << n << " " << m << endl;
        cout << r << " " << c << endl;
    } else {
        
        cout << n << " " << m << endl;

        
        
        vector<vector<int>> base(r, vector<int>(c));
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                base[i][j] = rnd.next(1, 1000000000);
            }
        }

        
        
        int temp_r = r;
        for (int i = 2; i * i <= temp_r; i++) {
            if (temp_r % i == 0) {
                int p = i;
                while (temp_r % i == 0) temp_r /= i;
                
                int step = r / p; 
                
                
                int row = rnd.next(step);
                int col = rnd.next(c);
                if (base[row][col] == base[row + step][col]) {
                    base[row][col]++;
                }
            }
        }
        if (temp_r > 1) { 
            int p = temp_r;
            int step = r / p;
            int row = rnd.next(step);
            int col = rnd.next(c);
            if (base[row][col] == base[row + step][col]) {
                base[row][col]++;
            }
        }

        
        int temp_c = c;
        for (int i = 2; i * i <= temp_c; i++) {
            if (temp_c % i == 0) {
                int p = i;
                while (temp_c % i == 0) temp_c /= i;
                
                int step = c / p;
                int row = rnd.next(r);
                int col = rnd.next(step);
                if (base[row][col] == base[row][col + step]) {
                    base[row][col]++;
                }
            }
        }
        if (temp_c > 1) {
            int p = temp_c;
            int step = c / p;
            int row = rnd.next(r);
            int col = rnd.next(step);
            if (base[row][col] == base[row][col + step]) {
                base[row][col]++;
            }
        }

        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cout << base[i % r][j % c] << (j == m - 1 ? "" : " ");
            }
            cout << "\n";
        }
    }

    return 0;
}