#include // #include // #include using namespace std; // using namespace atcoder; // using bint = boost::multiprecision::cpp_int; using ll = long long; using ull = unsigned long long; using P = pair; using vi = vector; using vvi = vector; using vvvi = vector; #define rep(i,n) for(ll i = 0;i < (ll)n;i++) #define ALL(x) (x).begin(),(x).end() #define sz(c) ((ll)(c).size()) #define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin()) #define UB(A,x) (int)(upper_bound(A.begin(),A.end(),x)-A.begin()) // #define MOD 1000000007 #define MOD 998244353 templateusing min_priority_queue=priority_queue,greater>; templateostream&operator<<(ostream&os,vector&v){for(int i = 0;i < v.size();i++)os<istream&operator>>(istream&is,vector&v){for(T&in:v)is>>in;return is;} templateostream&operator<<(ostream&os,pair&p){os<istream&operator>>(istream&is,pair&p){is>>p.first>>p.second;return is;} class UnionFind{ public: vector par; UnionFind(long long size) : par(size,-1){} bool unite(int x,int y){ x = root(x),y = root(y); if(x == y)return false; if(par[y] < par[x])swap(x,y); par[x] += par[y]; par[y] = x; return true; } bool find(int x,int y){ return root(x) == root(y); } int root(int x){ return par[x] < 0 ? x : par[x] = root(par[x]); } int size(int x){ return -par[root(x)]; } }; // https://beet-aizu.github.io/library/tree/heavylightdecomposition.cpp struct HLD{ /* v := 隣接リスト id := 元々の頂点のHLD後の頂点番号。 head := heavyな辺でつながれた頂点集合の代表元。もっとも浅い頂点。 sub_sz := 部分木のサイズ par := 親 inv := HLD後の頂点番号から元々の頂点番号をかえす add_edge(a,b) := a,b間の無向辺を追加 build() := HLD 分解を実行。内部でdfs_szによる部分木の取得、dfs_hldによるHLD実行。 lca(a,b) := a,bのLCAを返す for_each(a,b,f) := 頂点a,b間の頂点(a,bを含む)にfを実行。このfにseg木などが乗る。 for_each_edge(a,b,f) := 頂点a,b間の辺にfを実行。辺はつながっている2つの頂点の内深い方と対応させる。 */ vector> v; vector id,head,sub_sz,par,inv; HLD(int n) : v(n),id(n,-1),head(n),sub_sz(n,1),par(n,-1),inv(n){} void add_edge(int a,int b){ v[a].push_back(b); v[b].push_back(a); } void dfs_sz(int ov,int pre){ if(v[ov].size() && v[ov][0] == pre)swap(v[ov][0],v[ov].back()); par[ov] = pre; for(auto &nv : v[ov]){ if(nv == pre)continue; dfs_sz(nv,ov); sub_sz[ov] += sub_sz[nv]; if(sub_sz[nv] > sub_sz[v[ov][0]])swap(nv,v[ov][0]); } } void dfs_hld(int ov,int &pos){ id[ov] = pos++; inv[id[ov]] = ov; for(auto nv : v[ov]){ if(nv == par[ov])continue; head[nv] = (nv == v[ov][0] ? head[ov] : nv); dfs_hld(nv,pos); } } void build(int root = 0){ int pos = 0; dfs_sz(root,-1); head[root] = root; dfs_hld(root,pos); } int lca(int a,int b){ while(1){ if(id[a] > id[b])swap(a,b); if(head[a] == head[b])return a; b = par[head[b]]; } } /* 関数内でfは半開区間で値を取得するように正規化するので, BITが閉区間で実装されている場合などは auto f = [&](int a,int b){return bit.sum(a,b-1);}; の様に変更する.データ構造が半開区間で値を取得する場合は auto f = [&](int a,int b){return seg.get(a,b);}; でよい. 実装を見ればわかるが,a,bに対応する部分は hld.idで正規化されて渡されているので外部でfの宣言をするために正規化する必要はない. */ template void for_each(int a,int b,const F& f){ while(1){ if(id[a] > id[b])swap(a,b); f(max(id[head[b]],id[a]),id[b]+1); if(head[a] != head[b])b = par[head[b]]; else break; } } template void for_each_edge(int a,int b,const F& f){ while(1){ if(id[a] > id[b])swap(a,b); if(head[a] != head[b]){ f(id[head[b]],id[b]+1); b = par[head[b]]; }else{ if(a != b)f(id[a]+1,id[b]+1); break; } } } }; template class Segtree{ public: using F = function; int n; F f; T ti; vector dat; Segtree(){}; Segtree(F f,T ti): f(f),ti(ti){} void init(int N){ n = 1; while(n < N)n <<= 1; dat.assign(n << 1,ti); } void build(const vector &v){ int N = v.size(); init(N); for(int i = 0;i < N;i++)dat[n+i] = v[i]; for(int i = n-1;i >= 0;i--)dat[i] = f(dat[(i << 1)|0],dat[(i << 1)|1]); } void set(int k,T x){ dat[k+=n] = x; while(k >>= 1){ dat[k] = f(dat[(k << 1)|0],dat[(k << 1)|1]); } } T get(int l,int r){ T vl = ti,vr = ti; for(l += n,r += n;l < r;l >>= 1,r >>= 1){ if(l & 1)vl = f(vl,dat[l++]); if(r & 1)vr = f(dat[--r],vr); } return f(vl,vr); } // max t s.t. check(query(st,t)) template int bs(int st,C &check,T &acc,int k,int l,int r){ if(r-l <= 1){ acc = f(acc,dat[k]); return check(acc) ? k-n : -1; } int mid = (l+r) >> 1; if(mid <= st)return bs(st,check,acc,(k << 1)|1,mid,r); if(st <= l && !check(f(acc,dat[k]))){ acc = f(acc,dat[k]); return -1; } int vl = bs(st,check,acc,(k << 1)|0,l,mid); if(vl != -1)return vl; return bs(st,check,acc,(k << 1)|1,mid,r); } template int bs(int st,C &check){ T acc = ti; return bs(st,check,acc,1,0,n); } }; int dx[] = {-1,0,0,1}; int dy[] = {0,1,-1,0}; int main(){ ios_base::sync_with_stdio(0), cin.tie(0); int h,w; cin >> h >> w; vvi a(h,vi(w)); rep(i,h)cin >> a[i]; UnionFind uf(h*w+100); vvi e(h*w+1); rep(i,h)rep(j,w){ e[a[i][j]].emplace_back(i*w+j); } vector> v(h*w); rep(i,h*w+1){ for(auto au : e[i]){ int oi = au/w,oj = au%w; rep(_,4){ int ni = oi + dy[_],nj = oj + dx[_]; if(ni < 0 || ni >= h || nj < 0 || nj >= w)continue; if(a[ni][nj] <= i){ if(uf.unite(au,ni*w+nj)){ v[au].emplace_back(ni*w+nj,i); } } } } } HLD hld(h*w); rep(i,h*w){ for(auto au : v[i]){ hld.add_edge(au.first,i); } } hld.build(); auto f = [](ll a,ll b){return max(a,b);}; Segtree seg(f,-1); { vi init(h*w,-1); rep(i,h*w){ for(auto au : v[i]){ if(hld.par[i] == au.first){ init[hld.id[i]] = au.second; }else{ init[hld.id[au.first]] = au.second; } } } // cout << init << " a\n"; seg.build(init); } int Q;cin >> Q; while(Q--){ int rs,cs,rt,ct;cin >> rs >> cs >> rt >> ct; rs--;cs--;rt--;ct--; rs = rs*w+cs;rt = rt*w+ct; ll res = 0; auto g = [&](int l,int r){ res = max(res,seg.get(l,r)); }; hld.for_each_edge(rs,rt,g); cout << res << "\n"; } /* rep(i,h*w){ for(auto au : v[i]){ cout << i << " " << au << "\n"; } } */ return 0; }