#include <bits/stdc++.h>
using namespace std;

// The judge provides this.
long long value(int r, int c);

namespace {
    const long long INF = (long long)4e18;

    int N_global, M_global;
    map<pair<int,int>, long long> memo;

    long long getVal(int r, int c) {
        if (r < 1 || r > N_global || c < 1 || c > M_global) return INF;

        pair<int,int> p = {r, c};
        auto it = memo.find(p);
        if (it != memo.end()) return it->second;

        long long v = value(r, c);
        memo[p] = v;
        return v;
    }

    bool isLocalMin(int r, int c) {
        long long v = getVal(r, c);
        return v < getVal(r - 1, c) &&
               v < getVal(r + 1, c) &&
               v < getVal(r, c - 1) &&
               v < getVal(r, c + 1);
    }

    pair<int,int> solve(int top, int bottom, int left, int right) {
        // FIX ONLY HERE: single row
        if (top == bottom) {
            int row = top;
            int l = left, r = right;

            while (l <= r) {
                int mid = (l + r) / 2;

                long long cur = getVal(row, mid);
                long long lv = getVal(row, mid - 1);
                long long rv = getVal(row, mid + 1);

                if (cur < lv && cur < rv) {
                    return {row, mid};
                }

                if (lv < cur) r = mid - 1;
                else l = mid + 1;
            }
        }

        // unchanged from the first code
        if (left == right) {
            int col = left;
            int bestRow = top;
            for (int r = top; r <= bottom; r++) {
                if (getVal(r, col) < getVal(bestRow, col)) {
                    bestRow = r;
                }
            }
            return {bestRow, col};
        }

        int midRow = (top + bottom) / 2;
        int midCol = (left + right) / 2;

        // Find minimum on the middle cross
        pair<int,int> best = {midRow, left};
        for (int c = left; c <= right; c++) {
            if (getVal(midRow, c) < getVal(best.first, best.second)) {
                best = {midRow, c};
            }
        }
        for (int r = top; r <= bottom; r++) {
            if (getVal(r, midCol) < getVal(best.first, best.second)) {
                best = {r, midCol};
            }
        }

        int r = best.first;
        int c = best.second;

        if (isLocalMin(r, c)) return best;

        // Choose a smaller neighbor
        pair<int,int> nxt = {-1, -1};
        long long cur = getVal(r, c);

        const int dr[4] = {-1, 1, 0, 0};
        const int dc[4] = {0, 0, -1, 1};

        for (int k = 0; k < 4; k++) {
            int nr = r + dr[k];
            int nc = c + dc[k];
            if (nr < top || nr > bottom || nc < left || nc > right) continue;
            if (getVal(nr, nc) < cur) {
                nxt = {nr, nc};
                cur = getVal(nr, nc);
            }
        }

        // Recurse into the quadrant containing nxt
        int nr = nxt.first;
        int nc = nxt.second;

        if (nr < midRow && nc < midCol) {
            return solve(top, midRow - 1, left, midCol - 1);
        } else if (nr < midRow && nc > midCol) {
            return solve(top, midRow - 1, midCol + 1, right);
        } else if (nr > midRow && nc < midCol) {
            return solve(midRow + 1, bottom, left, midCol - 1);
        } else if (nr > midRow && nc > midCol) {
            return solve(midRow + 1, bottom, midCol + 1, right);
        } else if (nr == midRow) {
            if (nc < midCol) return solve(top, bottom, left, midCol - 1);
            else             return solve(top, bottom, midCol + 1, right);
        } else {
            if (nr < midRow) return solve(top, midRow - 1, left, right);
            else             return solve(midRow + 1, bottom, left, right);
        }
    }
}

pair<int, int> find_locmin_point(int N, int M) {
    N_global = N;
    M_global = M;
    memo.clear();
    return solve(1, N, 1, M);
}