题意:
给定一个n*n(n <= 300)的矩阵,然后是(T <= 10^6)次询问,每次询问某个子矩
阵中的最小值。
题解:建一颗二维线段树,表示这个矩阵的最小值,二维的线段树就可以解决,下面代码注释可以帮忙理解二维线段树的建树和查询
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=310;
const int inf=99999999;
struct Tree{
int Min;//当前节点区间最小值
int son[4];//四个儿子的编号
short x[2],y[2];//当前节点管理的x,y两个维度的区间
}T[maxn*maxn*5];
int Tree_Id;
int n;
int mat[maxn][maxn];
//fat为当前节点的坐标
void Build(int fat,int x0,int x1,int y0,int y1){
int i;
//*初始化操作
for(int 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(int 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);//第二个节点 联想一下4 5两个所以加判断
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) {
//如果在区间外直接返回一个inf最大值
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);
//访问它4个儿子
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;
}