//line 1 "answer.cpp" #include //line 5 "/home/seekworser/.cpp_lib/competitive_library/atcoder/dsu.hpp" namespace atcoder { // Implement (union by size) + (path compression) // Reference: // Zvi Galil and Giuseppe F. Italiano, // Data structures and algorithms for disjoint set union problems struct dsu { public: dsu() : _n(0) {} explicit dsu(int n) : _n(n), parent_or_size(n, -1) {} int merge(int a, int b) { assert(0 <= a && a < _n); assert(0 <= b && b < _n); int x = leader(a), y = leader(b); if (x == y) return x; if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y); parent_or_size[x] += parent_or_size[y]; parent_or_size[y] = x; return x; } bool same(int a, int b) { assert(0 <= a && a < _n); assert(0 <= b && b < _n); return leader(a) == leader(b); } int leader(int a) { assert(0 <= a && a < _n); if (parent_or_size[a] < 0) return a; return parent_or_size[a] = leader(parent_or_size[a]); } int size(int a) { assert(0 <= a && a < _n); return -parent_or_size[leader(a)]; } std::vector> groups() { std::vector leader_buf(_n), group_size(_n); for (int i = 0; i < _n; i++) { leader_buf[i] = leader(i); group_size[leader_buf[i]]++; } std::vector> result(_n); for (int i = 0; i < _n; i++) { result[i].reserve(group_size[i]); } for (int i = 0; i < _n; i++) { result[leader_buf[i]].push_back(i); } result.erase( std::remove_if(result.begin(), result.end(), [&](const std::vector& v) { return v.empty(); }), result.end()); return result; } private: int _n; // root node: -1 * component size // otherwise: parent std::vector parent_or_size; }; } // namespace atcoder //line 5 "/home/seekworser/.cpp_lib/competitive_library/atcoder/segtree.hpp" //line 2 "/home/seekworser/.cpp_lib/competitive_library/atcoder/internal_bit.hpp" #ifdef _MSC_VER #include #endif namespace atcoder { namespace internal { // @param n `0 <= n` // @return minimum non-negative `x` s.t. `n <= 2**x` int ceil_pow2(int n) { int x = 0; while ((1U << x) < (unsigned int)(n)) x++; return x; } // @param n `1 <= n` // @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` constexpr int bsf_constexpr(unsigned int n) { int x = 0; while (!(n & (1 << x))) x++; return x; } // @param n `1 <= n` // @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` int bsf(unsigned int n) { #ifdef _MSC_VER unsigned long index; _BitScanForward(&index, n); return index; #else return __builtin_ctz(n); #endif } } // namespace internal } // namespace atcoder //line 7 "/home/seekworser/.cpp_lib/competitive_library/atcoder/segtree.hpp" namespace atcoder { template struct segtree { public: S (*op)(S, S) = _op; S (*e)() = _e; segtree() : segtree(0) {} explicit segtree(const std::vector& v) : _n(int(v.size())) { log = internal::ceil_pow2(_n); size = 1 << log; d = std::vector(2 * size, e()); for (int i = 0; i < _n; i++) d[size + i] = v[i]; for (int i = size - 1; i >= 1; i--) { update(i); } } explicit segtree(int n) : segtree(std::vector(n, _e())) {} void set(int p, S x) { assert(0 <= p && p < _n); p += size; d[p] = x; for (int i = 1; i <= log; i++) update(p >> i); } void add(int p, S x) { assert(0 <= p && p < _n); (*this).set(p, (*this).get(p) + x); } S get(int p) const { assert(0 <= p && p < _n); return d[p + size]; } S prod(int l, int r) const { assert(0 <= l && l <= r && r <= _n); S sml = e(), smr = e(); l += size; r += size; while (l < r) { if (l & 1) sml = op(sml, d[l++]); if (r & 1) smr = op(d[--r], smr); l >>= 1; r >>= 1; } return op(sml, smr); } S all_prod() const { return d[1]; } template int max_right(int l) const { return max_right(l, [](S x) { return f(x); }); } template int max_right(int l, F f) const { assert(0 <= l && l <= _n); assert(f(e())); if (l == _n) return _n; l += size; S sm = e(); do { while (l % 2 == 0) l >>= 1; if (!f(op(sm, d[l]))) { while (l < size) { l = (2 * l); if (f(op(sm, d[l]))) { sm = op(sm, d[l]); l++; } } return l - size; } sm = op(sm, d[l]); l++; } while ((l & -l) != l); return _n; } template int min_left(int r) const { return min_left(r, [](S x) { return f(x); }); } template int min_left(int r, F f) const { assert(0 <= r && r <= _n); assert(f(e())); if (r == 0) return 0; r += size; S sm = e(); do { r--; while (r > 1 && (r % 2)) r >>= 1; if (!f(op(d[r], sm))) { while (r < size) { r = (2 * r + 1); if (f(op(d[r], sm))) { sm = op(d[r], sm); r--; } } return r + 1 - size; } sm = op(d[r], sm); } while ((r & -r) != r); return 0; } int n() const {return (*this)._n;} private: int _n, size, log; std::vector d; void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); } }; } // namespace atcoder //line 4 "answer.cpp" using namespace std; // using ll = long long; const int INF = 1 << 30; const vector> dxy = {{0,1},{0,-1},{1,0},{-1,0}}; int op_min(int a, int b) { return min(a, b); } int e_min() { return INF; } int op_max(int a, int b) { return max(a, b); } int e_max() { return -INF; } vector order; vector depth; vector visited; void eulertour(const vector> &g) { int n = g.size(); visited = vector(n, 2*n); auto dfs = [&](auto self, int u, int d, int parent) -> void { if (visited[u] == 2*n) visited[u] = order.size(); order.push_back(u); depth.push_back(d); for (int v : g[u]) if (v != parent) self(self, v, d+1, u); order.push_back(u); depth.push_back(d); }; dfs(dfs, 0, 0, -1); return; } int main() { int h,w; cin >> h >> w; vector a(h, vector(w)); for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { cin >> a[i][j]; } } vector> pos; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { pos.push_back({i,j}); } } sort(pos.begin(), pos.end(), [a](pair p, pair q) { return a[p.first][p.second] < a[q.first][q.second]; }); vector> g = [&]() { atcoder::dsu uf(h*w); vector> g(h*w); for (auto [i, j] : pos) { cout << i << " " << j << endl; for (auto [dx, dy] : dxy) { if (i+dx < 0 || i+dx >= h || j+dy < 0 || j+dy >= w) continue; int u = i*w+j; int v = (i+dx)*w+(j+dy); if (!uf.same(u, v)) { uf.merge(u, v); g[u].push_back(v); g[v].push_back(u); } } } return g; }(); eulertour(g); atcoder::segtree seg_depth(depth); auto lca = [&](int u, int v) -> int { if (visited[u] > visited[v]) swap(u, v); int min_depth = seg_depth.prod(visited[u], visited[v]+1); int lca_pos = seg_depth.max_right(visited[u], [&](int x) -> bool { return x > min_depth;}); return order[lca_pos]; }; int q; cin >> q; vector>> query(2*h*w); for (int i = 0; i < q; i++) { int rs, cs, rt, ct; cin >> rs >> cs >> rt >> ct; int u = (rs-1)*w+(cs-1); int v = (rt-1)*w+(ct-1); int l = lca(u, v); cout << u << " " << v << " " << l << endl; query[u].push_back({depth[visited[l]], i}); query[v].push_back({depth[visited[l]], i}); } atcoder::segtree seg_query(h*w); vector ans (q, INF); auto dfs = [&] (auto self, int u, int parent, int parent_val) -> void { int val = a[u/w][u%w]; seg_query.set(depth[visited[u]], min(val, parent_val)); cout << u << endl; for (int i = 0; i < h*w; i++) cout << seg_query.get(i) << " "; cout << endl; for (auto [d, id] : query[u]) { cout << d << " " << id << endl; ans[id] = min(ans[id], seg_query.prod(d+1, h*w)); } for (auto v : g[u]) if (v != parent) self(self, v, u, val); seg_query.set(depth[visited[u]], -INF); }; dfs(dfs, 0, -1, INF); for (auto x : ans) cout << x << '\n'; }