HDU 4819 Mosaic(二维线段树区间查询+单点更新模板)

The God of sheep decides to pixelate some pictures (i.e., change them into pictures with mosaic). Here's how he is gonna make it: for each picture, he divides the picture into n x n cells, where each cell is assigned a color value. Then he chooses a cell, and checks the color values in the L x L region whose center is at this specific cell. Assuming the maximum and minimum color values in the region is A and B respectively, he will replace the color value in the chosen cell with floor((A + B) / 2). 

Can you help the God of sheep?
InputThe first line contains an integer T (T ≤ 5) indicating the number of test cases. Then T test cases follow. 

Each test case begins with an integer n (5 < n < 800). Then the following n rows describe the picture to pixelate, where each row has n integers representing the original color values. The j-th integer in the i-th row is the color value of cell (i, j) of the picture. Color values are nonnegative integers and will not exceed 1,000,000,000 (10^9). 

After the description of the picture, there is an integer Q (Q ≤ 100000 (10^5)), indicating the number of mosaics. 

Then Q actions follow: the i-th row gives the i-th replacement made by the God of sheep: xi, yi, Li (1 ≤ xi, yi ≤ n, 1 ≤ Li < 10000, Li is odd). This means the God of sheep will change the color value in (xi, yi) (located at row xi and column yi) according to the Li x Li region as described above. For example, an query (2, 3, 3) means changing the color value of the cell at the second row and the third column according to region (1, 2) (1, 3), (1, 4), (2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4). Notice that if the region is not entirely inside the picture, only cells that are both in the region and the picture are considered. 

Note that the God of sheep will do the replacement one by one in the order given in the input.�� OutputFor each test case, print a line "Case #t:"(without quotes, t means the index of the test case) at the beginning. 

For each action, print the new color value of the updated cell. Sample Input
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 3
Sample 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;
}


你可能感兴趣的:(ACM,数据结构,线段树)