#include <iostream>
#include <cstring>
#include <stdio.h>
#include <set>
#include <algorithm>
using namespace std;

#define PLUS 2000
#define MaxDiff 2048
#define MaxN 1010
#define INF 1000000001

struct SET {
    int r,c;
    int v;

    SET() {}
    SET( int _r, int _c, int _v ) {
        r = _r;
        c = _c;
        v = _v;
    }

    bool operator < ( const SET &other ) const {
        if ( v != other.v ) return v < other.v;
        if ( r != other.r ) return r < other.r;
        return c < other.c;
    }
};

class SegmentTree {
public:
    int MIN[4*MaxDiff+10];

    SegmentTree() {
        for (int i = 0; i < 4*MaxDiff; ++i)
            MIN[i] = INF;
    }

    int Update( int node, int left, int right, int id, int val ) {
        if ( id < left || right < id ) return MIN[node];

        if ( left == right ) {
            return MIN[node] = val;
        }

        int mid = ( right + left ) >> 1;
        int p1 = Update( 2*node, left, mid, id, val );
        int p2 = Update( 2*node+1, mid+1, right, id, val );

        //if ( id == 2-3+PLUS ) printf("Update %d %d %d %d %d => %d %d\n",node,left,right,id,val,p1,p2);
        return MIN[node] = min( p1, p2 );
    }

    int Query( int node, int left, int right, int id )
    {
        //printf("%d %d %d %d | %d\n",node,left,right,id,MIN[node]);
        if ( MIN[node] >= id ) return INF;

        if ( left == right ) {
            return left;
        }

        int mid = ( right + left ) >> 1;
        int p1 = Query( 2*node, left, mid, id );
        if ( p1 != INF ) return p1;

        p1 = Query( 2*node, mid+1, right, id );
        return p1;
    }
};

set<SET> s[2*MaxDiff][4];
SegmentTree tree[4];

int n;
int a[MaxN][MaxN];
int q;
int o,r,c,v;

void Ubaci( int r, int c, int v )
{
    int broj = r + c;
    s[ broj + PLUS ][0].insert( SET( r, c, v ) );
    int val = (s[ broj + PLUS ][0].begin())->v;
    //printf("**** 1 ****\n");
    tree[0].Update( 1, 0, 2*MaxDiff, broj+PLUS, val );

    broj = c - r;
    s[ broj + PLUS ][1].insert( SET( r, c, v ) );
    val = (s[ broj + PLUS ][1].begin())->v;
    //printf("**** 2 ****\n");
    tree[1].Update( 1, 0, 2*MaxDiff, broj+PLUS, val );

    broj = r - c;
    s[ broj + PLUS ][2].insert( SET( r, c, v ) );
    val = (s[ broj + PLUS ][2].begin())->v;
    //printf("**** 3 ****\n");
    tree[2].Update( 1, 0, 2*MaxDiff, broj+PLUS, val );

    broj = -r - c;
    s[ broj + PLUS ][3].insert( SET( r, c, v ) );
    val = (s[ broj + PLUS ][3].begin())->v;
    //printf("**** 4 ****\n");
    tree[3].Update( 1, 0, 2*MaxDiff, broj+PLUS, val );
}

void Izbaci( int r, int c, int v )
{
    int broj = r + c;
    s[ broj + PLUS ][0].erase( s[broj+PLUS][0].find( SET( r, c, v ) ) );
    if ( !s[broj+PLUS][0].empty() ) {
        int val = (s[ broj + PLUS ][0].begin())->v;
        tree[0].Update( 1, 0, 2*MaxDiff, broj+PLUS, val );
    }

    broj = c - r;
    s[ broj + PLUS ][1].erase( s[broj+PLUS][1].find( SET( r, c, v ) ) );
    if ( !s[broj+PLUS][0].empty() ) {
        int val = (s[ broj + PLUS ][1].begin())->v;
        tree[1].Update( 1, 0, 2*MaxDiff, broj+PLUS, val );
    }

    broj = r - c;
    s[ broj + PLUS ][2].erase( s[broj+PLUS][2].find( SET( r, c, v ) ) );
    if ( !s[broj+PLUS][0].empty() ) {
        int val = (s[ broj + PLUS ][2].begin())->v;
        tree[2].Update( 1, 0, 2*MaxDiff, broj+PLUS, val );
    }

    broj = -r - c;
    s[ broj + PLUS ][3].erase( s[broj+PLUS][3].find( SET( r, c, v ) ) );
    if ( !s[broj+PLUS][0].empty() ) {
        int val = (s[ broj + PLUS ][3].begin())->v;
        tree[3].Update( 1, 0, 2*MaxDiff, broj+PLUS, val );
    }
}

int main()
{
    //freopen("pentranje.in","r",stdin);

    for (int i = 0; i < MaxDiff; ++i)
        for (int j = 0; j < 4; ++j)
            s[i][j].clear();

    scanf("%d",&n);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            scanf("%d",&a[i][j]);
            Ubaci( i, j, a[i][j] );
            //system("pause");
        }
    }

    scanf("%d",&q);
    for (int i = 0; i < q; ++i) {
        scanf("%d",&o);
        if ( o == 1 ) {
            scanf("%d%d",&r,&c);
            r--; c--;

            int ret = INF;
            //printf("**** 1 ****\n");
            int ret1 = min( ret, tree[0].Query( 1, 0, 2*MaxDiff, a[r][c] ) );
            //printf("**** 2 ****\n");
            int ret2 = min( ret, tree[1].Query( 1, 0, 2*MaxDiff, a[r][c] ) );
            //printf("**** 3 ****\n");
            int ret3 = min( ret, tree[2].Query( 1, 0, 2*MaxDiff, a[r][c] ) );
            //printf("**** 4 ****\n");
            int ret4 = min( ret, tree[3].Query( 1, 0, 2*MaxDiff, a[r][c] ) );

            if ( ret1 != INF ) ret = min( ret, r+c-ret1 );
            if ( ret2 != INF ) ret = min( ret, r+c-ret2 );
            if ( ret3 != INF ) ret = min( ret, r+c-ret3 );
            if ( ret4 != INF ) ret = min( ret, r+c-ret4 );

            if ( ret == INF ) ret = -1;
            printf("%d\n",ret);

        }
        else {
            scanf("%d%d%d",&r,&c,&v);
            r--; c--;

            Izbaci( r, c, a[r][c] );
            a[r][c] = v;
            Ubaci( r, c, v );
        }
    }

    return 0;
}
