方阵-二维线段树

Description

  为了准备校庆庆典,学校招募了一些学生组成了一个方阵,准备在庆典上演出。
  这个方阵是一个nm的矩形,第i行第j列有一名学生,他有一个能力值Ai,j。
  校长会定期检查一个p
q的方阵,询问这个方阵的学生能力值之和,或是学生能力值的最大值,或是学生能力值的最小值。由于校长不喜欢一个方阵长宽之比差太多,他每次询问的方阵的长不会超过宽的两倍。作为校庆筹办组组长的你,应该迅速并准确的回答校长所问的问题。

Input

  第一行包含两个整数n,m,表示这个方阵的两条边的长度。
  接下来n行,每行m个数,表示每个学生的能力值。
  接下来一行包含一个整数q,表示校长的询问数。
  接下来q行,每行先一个字符串s,接下来4个整数x1,y1,x2,y2,保证x1≤x2,y1≤y2,设以第x1行y1列为左上角,第x2行y2列为右下角的方阵为P。(本题为0下标)
  若字符串内容为“SUM”,请求出P中所有学生的能力值之和。
  若字符串内容为“MAX”,请求出P中所有学生的能力值的最大值。
  若字符串内容为“MIN”,请求出P中所有学生的能力值的最小值。

Output

  输出总共q行,第i行的数为第i组询问对应的答案ansi。

Sample Input

3 3
1 2 3
4 5 6
7 8 9
3
SUM 0 0 1 1
MAX 0 0 2 2
MIN 0 1 1 1

Sample Output

12
9
2

Hint

【样例解释】
  对于第一组询问,能力值之和为1+2+4+5=12。
  对于第二组询问,能力值最大的位置为第2行第2列。
  对于第三组询问,能力值最小的位置为第0行第1列。
【数据规模和约定】
  对于40%的数据,n,m≤200,q≤200
  对于60%的数据,n,m≤300,q≤100000
  对于80%的数据,n,m≤500,q≤500000
  对于100%的数据,n,m≤800,q≤500000,0≤Aij≤3000,每个询问的方阵的长不超过宽的两倍。


思路

  • 二维线段树或二维RMQ模板
  • 代码为二维线段树

代码

#include <iostream>
#include <cstdio>
using namespace std;
const int inf=3005;
const int maxn=802;
int n,m,q,sum[maxn][maxn],ansmax,ansmin;
struct node_y{short maxx,minn,l,r;};
struct node_x{node_y f[maxn<<2];short l,r;}a[maxn<<2];
short max(short a,short b){return a>b?a:b;}
short min(short a,short b){return a<b?a:b;}
void pushup_y(int x,int v) {
	a[v].f[x].maxx=max(a[v].f[x<<1].maxx,a[v].f[x<<1|1].maxx);
	a[v].f[x].minn=min(a[v].f[x<<1].minn,a[v].f[x<<1|1].minn);
}
void pushup_x(int x,int v){
	a[v].f[x].maxx=max(a[v<<1].f[x].maxx,a[v<<1|1].f[x].maxx);
	a[v].f[x].minn=min(a[v<<1].f[x].minn,a[v<<1|1].f[x].minn);
}
void build_y(int x,int left,int right,int v,int lx)
{
	a[v].f[x].l=left; a[v].f[x].r=right;
	if(left==right) {
		if(lx!=-1) a[v].f[x].maxx=a[v].f[x].minn=sum[lx][left];
		else pushup_x(x,v);
		return;
	}
	int mid=(left+right)>>1;
	build_y(x<<1,left,mid,v,lx); build_y(x<<1|1,mid+1,right,v,lx);
	pushup_y(x,v);
}
void build_x(int x,int left,int right)
{
	a[x].l=left; a[x].r=right; 
	if(left==right) {build_y(1,1,m,x,left); return;}
	int mid=(left+right)>>1;
	build_x(x<<1,left,mid); build_x(x<<1|1,mid+1,right);
	build_y(1,1,m,x,-1);
}
void query_y(int x,int left,int right,int v)
{
	if(a[v].f[x].r<left||a[v].f[x].l>right) return;
	if(left<=a[v].f[x].l&&right>=a[v].f[x].r){
		ansmax=max(ansmax,a[v].f[x].maxx);
		ansmin=min(ansmin,a[v].f[x].minn);
		return;
	}
	query_y(x<<1,left,right,v); query_y(x<<1|1,left,right,v);
}
void query_x(int x,int xl,int xr,int yl,int yr)
{
	if(a[x].r<xl||a[x].l>xr) return;
	if(xl<=a[x].l&&xr>=a[x].r){query_y(1,yl,yr,x); return;}
	query_x(x<<1,xl,xr,yl,yr); query_x(x<<1|1,xl,xr,yl,yr);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j) scanf("%d",&sum[i][j]);
	build_x(1,1,n);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j) sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
	scanf("%d",&q);
	while(q--)
	{
		char f[5]; int t1,t2,t3,t4; scanf("%s%d%d%d%d",&f,&t1,&t2,&t3,&t4); ++t1;++t2;++t3;++t4;
		if(f[0]=='S') cout<<sum[t3][t4]-sum[t3][t2-1]-sum[t1-1][t4]+sum[t1-1][t2-1]<<'
';
		else if(f[1]=='A') ansmax=-inf,query_x(1,t1,t3,t2,t4),cout<<ansmax<<'
';
		else ansmin=inf,query_x(1,t1,t3,t2,t4),cout<<ansmin<<'
';
	}
	return 0;
}
原文地址:https://www.cnblogs.com/wuwendongxi/p/14056283.html