#include <iostream>
#include <fstream>
#include <stack>
#include <vector>
#include <deque>
using namespace std;
const int inf = 3e4 + 5;
const int NMax = 1e3 + 5;
class parser { // pentru parsare
using integer = short;
static const int strMax = 6*NMax*NMax + 50;
ifstream in;
char str[strMax],*p;
public:
parser(const string& s) {
in.open(s);
in.get(str,strMax,1);
p = str;
}
parser& operator>> (integer& nr) {
while ( !( ('0' <= *p && *p <= '9') || *p == '\0') ) {
++p;
}
int temp = 0;
while ('0' <= *p && *p <= '9') {
temp = temp * 10 + *p++ - '0';
}
nr = temp;
return *this;
}
void close() {
in.close();
}
};
parser pin("struti.in");
ofstream out("struti.out");
#define ll long long
#define ull unsigned long long
#define pb push_back
using zint = short;
zint N,M,P;
zint a[NMax][NMax],rmn[NMax][NMax],rmx[NMax][NMax];
pair<zint,zint> dmn[NMax],dmx[NMax];
const pair<zint,zint> def = pair<zint,zint>(1,0);
// a[i][j] - altitudinea din zona (i,j);
// rmn[i][j] = min( a[i][j-dy+1], a[i][j-dy+2], ..., a[i][j] );
// rmx[i][j] = max( a[i][j-dy+1], a[i][j-dy+2], ..., a[i][j] );
// unde dx,dy depind de query;
// dmn - deque implementat manual pentru determinarea minimului pe o secventa de lungime dx/dy;
// dmx - deque implementat manual pentru determinarea maximului pe o secventa de lungime dx/dy;
// def - valoare pentru a reseta un deque;
// deque[0].first = pozitia pe care se afla primul element al deque-ului, cel din "fata";
// deque[0].second = pozitia pe care se afla ultimul element al deque-ului, cel din "spate";
// else:
// pair.first = indexul liniei/coloanei de pe care s-a obtinut valoarea pair.second;
void solve(zint,zint,zint&,int&);
void insertMax(pair<zint,zint>*,zint,zint,zint);
void insertMin(pair<zint,zint>*,zint,zint,zint);
int main() {
pin>>N>>M>>P;
for (zint i=1;i <= N;++i) {
for (zint j=1;j <= M;++j) {
pin>>a[i][j];
}
}
while (P--) {
zint dx,dy,min1,min2;
int nrMin1,nrMin2;
pin>>dx>>dy;
solve(dx,dy,min1,nrMin1);
if (dx != dy) {
solve(dy,dx,min2,nrMin2);
if (min1 == min2) {
nrMin1 += nrMin2;
}
else if (min1 > min2) {
min1 = min2;
nrMin1 = nrMin2;
}
}
out<<min1<<' '<<nrMin1<<'\n';
}
pin.close();out.close();
return 0;
}
// gaseste minimul unei diferente de altitudine dintr-un
// dreptunghi de dimensiune (dx,dy), cat si numarul acestor dreptunghiuri
// si le pune in mn,nrMin;
void solve(zint dx,zint dy,zint& mn,int& nrMin) {
for (zint i=1;i <= N;++i) {
dmn[0] = dmx[0] = def;
for (zint j=1;j <= M;++j) {
insertMin(dmn,j,a[i][j],dy);
insertMax(dmx,j,a[i][j],dy);
rmn[i][j] = dmn[ dmn[0].first ].second;
rmx[i][j] = dmx[ dmx[0].first ].second;
}
}
mn = inf, nrMin = 0;
for (zint j=dy;j <= M;++j) {
dmn[0] = dmx[0] = def;
for (zint i=1;i <= N;++i) {
insertMin(dmn,i,rmn[i][j],dx);
insertMax(dmx,i,rmx[i][j],dx);
if (i >= dx) {
zint val = dmx[ dmx[0].first ].second - dmn[ dmn[0].first ].second;
if (mn > val) {
mn = val;
nrMin = 1;
}
else if (mn == val) {
++nrMin;
}
}
}
}
}
// adauga o valoare intr-un deque menit sa tina maximul pe o secventa;
void insertMax(pair<zint,zint> *d,zint idx,zint val,zint len) {
while (d[0].first <= d[0].second && d[ d[0].second ].second <= val) {
--d[0].second;
}
d[ ++d[0].second ] = pair<zint,zint>(idx,val);
if (d[ d[0].first ].first == idx - len) {
++d[0].first;
}
}
// adauga o valoare intr-un deque menit sa tina minimul pe o secventa;
void insertMin(pair<zint,zint> *d,zint idx,zint val,zint len) {
while (d[0].first <= d[0].second && d[ d[0].second ].second >= val) {
--d[0].second;
}
d[ ++d[0].second ] = pair<zint,zint>(idx,val);
if (d[ d[0].first ].first == idx - len) {
++d[0].first;
}
}