#include <cstdio>
#include <vector>
#include <algorithm>
struct Query {
int X1, Y1, X2, Y2;
int Answer, Index;
Query() {}
Query( int X1, int Y1, int X2, int Y2, int Answer, int Index ) {
this -> X1 = X1; this -> Y1 = Y1;
this -> X2 = X2; this -> Y2 = Y2;
this -> Answer = Answer;
this -> Index = Index;
}
}; std::vector <Query> MyQuerys;
struct Element {
int Value, X, Y;
Element() {}
Element( int Value, int X, int Y ) {
this -> Value = Value;
this -> X = X; this -> Y = Y;
}
}; std::vector <Element> MyElements;
std::vector <int> DisjointSet;
std::vector < std::vector <int> > Matrix;
int Dx[4] = { -1, 0, 1, 0 };
int Dy[4] = { 0, 1, 0, -1 };
inline bool comp1( Element MyElement1, Element MyElement2 ) {
return MyElement1.Value > MyElement2.Value;
}
inline bool comp2( Query MyQuery1, Query MyQuery2 ) {
return MyQuery1.Answer > MyQuery2.Answer;
}
inline bool comp3( Query MyQuery1, Query MyQuery2 ) {
return MyQuery1.Index < MyQuery2.Index;
}
inline int root( int Pos ) {
return ( DisjointSet[Pos] < 0 ) ? Pos : root( DisjointSet[Pos] );
}
void SolveTestCase( void ) {
int N, Q, X1, Y1, X2, Y2, X, Y;
scanf( "%d %d", &N, &Q );
MyElements.clear(); MyQuerys.clear();
DisjointSet.resize( N * N );
Matrix.resize( N );
for( int i = 0; i < N; i ++ ) {
Matrix[i].resize( N );
for( int j = 0; j < N; j ++ ) {
scanf( "%d", &Matrix[i][j] );
MyElements.push_back( {Matrix[i][j], i, j} );
}
}
for( int i = 0; i < Q; i ++ ) {
scanf( "%d %d %d %d", &X1, &Y1, &X2, &Y2 );
MyQuerys.push_back( {X1 - 1, Y1 - 1, X2 - 1, Y2 - 1, 0, i} );
}
for( int k = (1 << 20); k; k /= 2 ) {
std::sort( MyElements.begin(), MyElements.end(), comp1 );
std::sort( MyQuerys.begin(), MyQuerys.end(), comp2 );
std::fill( DisjointSet.begin(), DisjointSet.end(), -1 );
int Cursor = 0;
for( int i = 0; i < MyElements.size(); i ++ ) {
X = MyElements[i].X; Y = MyElements[i].Y;
for( int d = 0; d < 4; d ++ ) {
int X1 = X + Dx[d], Y1 = Y + Dy[d];
if( X1 < 0 || X1 >= N ) continue;
if( Y1 < 0 || Y1 >= N ) continue;
if( Matrix[X1][Y1] < Matrix[X][Y] ) continue;
int Pos1 = root( X * N + Y ), Pos2 = root( X1 * N + Y1 );
if( Pos1 != Pos2 ) {
if( DisjointSet[Pos1] < DisjointSet[Pos2] ) {
DisjointSet[Pos1] += DisjointSet[Pos2];
DisjointSet[Pos2] = Pos1;
} else {
DisjointSet[Pos2] += DisjointSet[Pos1];
DisjointSet[Pos1] = Pos2;
}
}
}
if( i < MyElements.size() && Cursor < MyQuerys.size() && MyElements[i + 1].Value >= MyQuerys[Cursor].Answer + k )
continue;
while( Cursor < MyQuerys.size() && MyElements[i].Value <= MyQuerys[Cursor].Answer + k ) {
int Pos1 = root( MyQuerys[Cursor].X1 * N + MyQuerys[Cursor].Y1 );
int Pos2 = root( MyQuerys[Cursor].X2 * N + MyQuerys[Cursor].Y2 );
if( Pos1 == Pos2 )
MyQuerys[Cursor].Answer += k;
Cursor ++;
}
}
}
std::sort( MyQuerys.begin(), MyQuerys.end(), comp3 );
for( int i = 0; i < MyQuerys.size(); i ++ )
printf( "%d\n", MyQuerys[i].Answer );
return;
}
int main( int argc, const char *argv[] ) {
int T = 1;
freopen( "matrice2.in" , "r", stdin );
freopen( "matrice2.out", "w", stdout );
for( int i = 1; i <= T; i ++ )
SolveTestCase();
return 0;
}