1 3 1 2 3 4 5 6 7 8 9 5 2 2 1 3 2 3 1 1 3 1 2 3 2 2 3Sample Output
Case #1: 5 6 3 4 6
题解:
题意:
就是给你一个n*n的矩阵,每次询问一个点的坐标,和l,以该点坐标为中心搞一个长宽都为l的正方形矩阵,求该矩阵的最大值和最小值,然后修改该点的值为最大值和最小值的平均值
之前只做过二维的树状数组,二维线段树真不会啊,我直接搜了个博客,看懂了以后直接复制了他的博客,因为原文注释比较少,所以我就加上了我的注释,也是我的理解。。。真想知道世界上第一个想出二维线段树的人是这么写出来的
原博客博主:http://blog.csdn.net/htt_h/article/details/44944039
ps:
强迫症的我删掉了他的所有空格,把位运算改成了加和乘运算,然后代码对齐了。。。。
代码:
#include
#include
#include
#include
using namespace std;
#define maxn 801
#define inf 0x3f3f3f3f
int maxi[maxn*3][maxn*3];
int mini[maxn*3][maxn*3];
int n;
int val;//修改的值
int x,y;//修改点,设为全局变量就不用传到参数里
int x1,x2,y1,y2;//查询的区间
int crx;//只表示一行的外层线段树节点编号
int cmax,cmin;//记录查询区间的最大值最小值
void buildy(int rx,int ry,int l,int r)//构造行
{
if(l==r)//如果当前行长度为1
{
if(rx==crx)//当前列的长度也为1,则表示坐标点,直接赋值
{
scanf("%d",&mini[rx][ry]);
maxi[rx][ry]=mini[rx][ry];
}
else//区间列的长度不为1,则为一列的最值,由外层的那列左右子树的最值来pushup
{
int lrx=rx*2,rrx=rx*2+1;
maxi[rx][ry]=max(maxi[lrx][ry],maxi[rrx][ry]);
mini[rx][ry]=min(mini[lrx][ry],mini[rrx][ry]);
}
return;
}
int m =(l+r)/2;
int lc=ry*2,rc=ry*2+1;
buildy(rx,lc,l,m);//行长度不为1继续分下去
buildy(rx,rc,m+1,r);
maxi[rx][ry]=max(maxi[rx][lc],maxi[rx][rc]);//区间行的长度不为1,则为一行的最值,用行的左右子树来更新区间
mini[rx][ry]=min(mini[rx][lc],mini[rx][rc]);
}
void buildx(int rx,int l,int r)//构造列
{
int m =(l+r)/2;
if(r>l)//如果不是一列,就继续二分建树
{
buildx(rx*2,l,m);
buildx(rx*2+1,m+1,r);
}
if(l==r){crx=rx;x=l;}//如果列的长度为1,就记录当前节点列的标号,用于在子树中判断是否为一列
buildy(rx,1,1,n);//构造行
}
void updatey(int rx,int ry,int l,int r)//更新行
{
if(l==r)//行的长度为1
{
if(rx==crx)//列的长度为1,代表是要更新的点
maxi[rx][ry]=mini[rx][ry]=val;
else{//否则继续划分列,直到查到了该点的列为止
int lrx=rx*2,rrx=rx*2+1;
maxi[rx][ry]=max(maxi[lrx][ry],maxi[rrx][ry]);//这时列的长度不为1,为一列的最值,由列的左右子树更新
mini[rx][ry]=min(mini[lrx][ry],mini[rrx][ry]);
}
return;
}
int m=(l+r)/2;
int lc=ry*2,rc=ry*2+1;
if(y<=m)//查找该行
updatey(rx,lc,l,m);
else
updatey(rx,rc,m+1,r);
maxi[rx][ry]=max(maxi[rx][lc],maxi[rx][rc]);//这时行的长度不为1就是由行的左右子树更新
mini[rx][ry]=min(mini[rx][lc],mini[rx][rc]);
}
void updatex(int rx, int l, int r)//更新列
{
int m=(l+r)/2;
int lc=rx*2,rc=rx*2+1;
if(r>l)//列的长度大于1就继续分
{
if(x<=m)
updatex(lc,l,m);
else
updatex(rc,m+1,r);
}
if(l==r)
crx=rx;
updatey(rx,1,1,n);//查找到该列就更新行
}
void qy(int rx,int ry,int l,int r)//查询行
{
if(y1<=l&&y2>=r)//询问到了询问的子区间
{
cmin=min(cmin,mini[rx][ry]);
cmax=max(cmax,maxi[rx][ry]);
return;
}
int m=(l+r)/2;
int lc=ry*2,rc=ry*2+1;
if(y1<=m)
qy(rx,lc,l,m);
if(y2>m)
qy(rx,rc,m+1,r);
}
void qx(int rx,int l,int r)//询问列
{
if(x1<=l&&x2>=r)//询问到了列的子区间,询问行
{
qy(rx,1,1,n);
return;
}
int m =(l+r)/2;
int lc=rx*2,rc=rx*2+1;
if(x1<=m)
qx(lc,l,m);
if(x2>m)
qx(rc,m+1,r);
}
int main()
{
int t,cnt=1;
scanf("%d",&t);
while(t--)
{
memset(maxi,-inf,sizeof(maxi));
memset(mini,inf,sizeof(mini));
printf("Case #%d:\n",cnt++);
scanf("%d",&n);
buildx(1,1,n);
int l;
int q;
scanf("%d",&q);
while(q--)
{
scanf("%d%d%d",&x,&y,&l);
l/=2;
x1=max(1,x-l);x2=min(n,x+l);
y1=max(1,y-l);y2=min(n,y+l);
cmax=-inf,cmin=inf;qx(1,1,n);//初始化最大最小为负无穷和正无穷
val=(cmin+cmax)/2;
printf("%d\n",val);
crx=-1;updatex(1,1,n);
}
}
return 0;
}