我越来越觉得之前自己的代码写的就跟shi一样……
题意很简单,矩阵中指定一个方形,查询出这个方形中的最大值和最小值,然后把矩阵中间那个点改成它们的平均数。这个就是很明显的二维线段树了……
这种改单点,问区间的二维线段树在建立和修改的时候有个问题:
0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 0 | 6 | 0 | 0 |
0 | 0 | 0 | 0 | 0 |
以纵向为X轴,横向为Y轴建立二维线段树,考虑一下,当我把(4,3)这个点改成6的时候,我肯定是要深入到X=4这个节点去改的,然后第4行这个线段树对了;之后我回溯到X=4~5这个节点的时候,就会有点麻烦,你会发现X=4~5的3号位置不好填了,因为X=4的这个节点改了3这个位置以后,并没有通知到他上层的节点。
解决方法是什么呢?我们一定要注意,当Y轴长度为1的时候,X轴方向也是一颗线段树。而且,不同的X节点中,Y轴的那个指针数字相同的,其LR一定是一样的。比如所有的1号Y节点都是表示1~N的。所以,我们在不是叶子节点的X轴节点处深入修改Y节点时,当Y节点已经修改到叶子了,就是当你正要更新(4,3)~(5,3)这个矩形的时候,我们再将纵向看成一棵线段树,去询问X轴方向的相应位置的子树的最值就可以了。这里我们只需要去询问(4,3)(5,3)两个X轴方向的叶子节点,就能完成更新。
由于X轴是自底向上更新的,所以当你询问一个X轴长度为2,Y轴长度为1的矩形的时候,那两个X轴长度为1的肯定已经是对的了;同理,当你问X轴长度为4的矩形的时候,那两个长度为2的子线段也肯定是对的。所以你只需要直接从两个儿子更新一下即可。
在区间查询的时候,比如我查(4,1)~(5,5)这个矩形,到X=4~5处停下就可以了,因为X=4~5里面的信息肯定是对的。
这就是单点修改,区间查询线段树最方便的维护方法。
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN = 810;
const int INF = 1<<30;
struct ntype
{
int max_value;
int min_value;
};
ntype node[4*MAXN][4*MAXN];//不定义两种结构体,否则写起来太长,第一维是X轴,第二维是Y轴
int n;
int t;
int q;
int map[1000][1000];
int ask_min_y(int l, int r, int py, int px, int lft, int rght)
{
if(lft <=l && r<=rght)
{
return node[px][py].min_value;
}
int r1=INF, r2=INF;
int mid = (l+r)/2;
if(lft <= mid)
r1 = ask_min_y(l, mid, py*2, px, lft, rght);
if(rght >= mid+1)
r2 = ask_min_y(mid+1, r, py*2+1, px, lft, rght);
return min(r1, r2);
}
int ask_min(int l, int r, int p, int up, int down, int lft, int rght)
{
if(up<=l && r<=down)
{
int r= ask_min_y(1,n,1, p, lft, rght);
return r;
}
int r1=INF, r2=INF;
int mid = (l+r)/2;
if(up<=mid)
r1 = ask_min(l, mid, p*2, up, down, lft, rght);
if(down >= mid+1)
r2 = ask_min(mid+1, r, p*2+1, up, down, lft, rght);
return min(r1, r2);
}
int ask_max_y(int l, int r, int py, int px, int lft, int rght)
{
if(lft <=l && r<=rght)
{
return node[px][py].max_value;
}
int r1=-INF, r2=-INF;
int mid = (l+r)/2;
if(lft <= mid)
r1 = ask_max_y(l, mid, py*2, px, lft, rght);
if(rght >= mid+1)
r2 = ask_max_y(mid+1, r, py*2+1, px, lft, rght);
return max(r1, r2);
}
int ask_max(int l, int r, int p, int up, int down, int lft, int rght)
//询问最大值,目前范围l~r, 指针=p,上界是up……等等
//这个按照普通的样子写就可以了
{
if(up<=l && r<=down)
{
int ret= ask_max_y(1, n, 1, p, lft, rght);
return ret;
}
int r1=-INF, r2=-INF;
int mid = (l+r)/2;
if(up<=mid)
r1 = ask_max(l, mid, p*2, up, down, lft, rght);
if(down >= mid+1)
r2 = ask_max(mid+1, r, p*2+1, up, down, lft, rght);
return max(r1, r2);
}
void update_y(int l, int r, int py, int px, bool x_isleaf, int tar_y, int tar_v)
//update_y函数基本原理和build_y函数几乎完全相同
{
if( l==r && l==tar_y)
{
if(x_isleaf)
{
node[px][py].max_value = tar_v;
node[px][py].min_value = tar_v;
}
else
{
node[px][py].max_value = max(node[px*2][py].max_value, node[px*2+1][py].max_value);
node[px][py].min_value = min(node[px*2][py].min_value, node[px*2+1][py].min_value);
}
return;
}
int mid = (l+r)/2;
if(tar_y <= mid)
update_y(l, mid, py*2, px, x_isleaf, tar_y, tar_v);
else
update_y(mid+1, r, py*2+1, px, x_isleaf, tar_y, tar_v);
node[px][py].max_value = max(node[px][py*2].max_value, node[px][py*2+1].max_value);
node[px][py].min_value = min(node[px][py*2].min_value, node[px][py*2+1].min_value);
return;
}
void update(int l, int r, int p, int tar_x, int tar_y, int tar_v)
//目前范围:l~r,指针=p,要修改的点是tar_x, tar_y, 修改值为v
{
if(l==r && l == tar_x)
{
update_y(1, n, 1, p, true, tar_y, tar_v);
return;
}
int mid=(l+r)/2;
if(tar_x <= mid)
update(l, mid, p*2, tar_x, tar_y, tar_v);
else
update(mid+1, r, p*2+1, tar_x, tar_y, tar_v);
update_y(1, n, 1, p, false, tar_y, tar_v);
return;
}
void init()
{
memset(node, 0, sizeof(node));
return;
}
void build_y(int l, int r, int py, int px, bool x_isleaf, int row_num)
//目前范围:l~r,y指针=py,x指针=px
//x_isleaf 代表目前是否是一个X轴的叶子,row_num=列号,只有在x_isleaf=true的时候有意义
{
if(l==r)
{
if(x_isleaf)
{
node[px][py].max_value = map[row_num][l];
node[px][py].min_value = map[row_num][l];
}
else //当前的Y轴叶子节点实际上包含一段X区间,去根据这个区间拿到我的值
{
node[px][py].max_value = max(node[px*2][py].max_value, node[px*2+1][py].max_value);
node[px][py].min_value = min(node[px*2][py].min_value, node[px*2+1][py].min_value);
}
return;
}
int mid = (l+r)/2;
build_y(l, mid, py*2, px, x_isleaf, row_num);
build_y(mid+1, r, py*2+1, px, x_isleaf, row_num);
node[px][py].max_value = max(node[px][py*2].max_value, node[px][py*2+1].max_value);
node[px][py].min_value = min(node[px][py*2].min_value, node[px][py*2+1].min_value);
return;
}
void build(int l, int r, int p)//目前范围:l~r, 指针=p
{
if(l==r)
{
build_y(1, n, 1, p, true, l);//X轴建树到叶子节点,建Y
return;
}
int mid = (l+r)/2;
build(l,mid, p*2);
build(mid+1, r, p*2+1);
build_y(1, n, 1, p, false, l);//先建好X方向的子树再建Y,才能保证自己Y方向更新对
return;
}
int main()
{
scanf("%d", &t);
int files;
for(files=1; files<=t; files++)
{
init();
printf("Case #%d:\n", files);
scanf("%d", &n);
int i,j;
for(i=1; i<=n;i++)
{
for(j=1; j<=n;j++)
{
scanf("%d", &map[i][j]);
}
}
build(1, n, 1);
scanf("%d", &q);
for(i=1; i<=q; i++)
{
int x, y, l;
scanf("%d %d %d", &x, &y, &l);
l--;
int range_l = max(1, y - l/2);
int range_r = min(n, y + l/2);
int range_u = max(1, x - l/2);
int range_d = min(n, x + l/2);
int min1 = ask_min(1, n, 1, range_u, range_d, range_l, range_r);
int max1 = ask_max(1, n, 1, range_u, range_d, range_l, range_r);
int now = (min1 + max1) / 2;
printf("%d\n", now);
update(1, n, 1, x, y, now);
}
}
//system("pause");
return 0;
}