|
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2859
/**//* 题意: 给定一个n*n(n <= 300)的矩阵,然后是(T <= 10^6)次询问,每次询问某个子矩 阵中的最小值。
解法: 二维线段树 或者 二维RMQ
思路: 一维线段树是一颗完全二叉树,那么二维线段树无疑就是一颗完全四叉树,换言 之,每个结点有四个儿子,这里为了空间不浪费,将所有结点记录在一个一维数组中 ,每个结点的四个儿子采用编号的方式存储,在建树之前将每个结点的信息全部初始 化,初始化的时候需要注意的是每次将当前结点的四个儿子清空,然后判断它本身是 否是叶子结点,可以通过x和y区间端点是否重合来判断,最后再来生成四个儿子编号 ,然后往下递归,递归结束后根据四个儿子的最小值更新当前的最小值。再来看询问 ,和一维的情况类似,一维是对区间交,而二维则是对矩形交,如果询问的二维区间 和当前结点管理的二维区间没有交集,显然不可能有最小值,直接返回inf,否则如果 询问的二维区间完全包含了当前结点管理的二维区间,那么返回结点最小值。否则递 归当前结点的四个儿子,取最小值,回归到根节点就得到了询问区间的最值了。 需要注意的是在建树的时候不要在叶子结点生成多余的儿子结点,这样内存会多 一倍,如果开得不够大有可能下标越界,开得太大有可能超内存。还有就是在二维线 段树的结点上信息会多了不少,能节省空间尽量节省,比如每个结点管理的区间端点 不可能很大,所以不需要int,short就足够了。 */
#include <iostream> #include <cstring> #include <cstdio>
using namespace std;
#define maxn 310 #define inf (1<<30)-1
struct Tree { int Min; // 当前结点区间最小值 int son[4]; // 四个儿子在数组中的编号 short x[2], y[2]; // 当前结点管理的二维区间 }T[maxn*maxn*5];
int Tree_Id;
int n; int mat[maxn][maxn];
void Build(int fat, int x0, int x1, int y0, int y1) { int i; for(i = 0; i < 4; i++) { T[fat].son[i] = 0; } T[fat].x[0] = x0; T[fat].x[1] = x1; T[fat].y[0] = y0; T[fat].y[1] = y1;
if(x0 == x1 && y0 == y1) { T[fat].Min = mat[x0][y0]; return ; } for(i = 0; i < 4; i++) { T[fat].son[i] = Tree_Id++; }
int xmid = (x0 + x1) >> 1; int ymid = (y0 + y1) >> 1; Build(T[fat].son[0], x0, xmid, y0, ymid); Build(T[fat].son[1], x0, xmid, (ymid<y1) ? ymid+1 : ymid, y1); Build(T[fat].son[2], (xmid<x1) ? xmid+1 : xmid, x1, y0, ymid); Build(T[fat].son[3], (xmid<x1) ? xmid+1 : xmid, x1, (ymid<y1) ? ymid+1 : ymid, y1);
T[fat].Min = T[T[fat].son[0]].Min; for(i = 1; i < 4; i++) { if(T[T[fat].son[i]].Min < T[fat].Min) T[fat].Min = T[T[fat].son[i]].Min; } }
int Query(int fat, int x0, int x1, int y0, int y1) { if(x0 > T[fat].x[1] || x1 < T[fat].x[0] || y0 > T[fat].y[1] || y1 < T[fat].y[0]) { return inf; }
if(x0 <= T[fat].x[0] && T[fat].x[1] <= x1 && y0 <= T[fat].y[0] && T[fat].y[1] <= y1) { return T[fat].Min; } int i; int Min = inf; for(i = 0; i < 4; i++) { int v = Query(T[fat].son[i], x0, x1, y0, y1); if(v < Min) Min = v; } return Min; }
int main() { int t; int i, j;
scanf("%d", &t); while(t--) { scanf("%d", &n); Tree_Id = 0; for(i = 1; i <= n; i++) { for(j = 1; j <= n; j++) { scanf("%d", &mat[i][j]); } } Tree_Id = 2; Build(1, 1, n, 1, n);
int m; scanf("%d", &m);
while(m--) { int r[2], c[2]; scanf("%d %d %d %d", &r[0], &c[0], &r[1], &c[1]); printf("%d\n", Query(1, r[0], r[1], c[0], c[1])); } } return 0; }
|