学习笔记:线段树套线段树(二维线段树)

看了董晓老师的博客,但是没找到洛谷题,实在不想读英文(不是,写点理解巩固一下这方面的知识;

前置知识:具有线段树的基础,并有一定理解(能一定程度上的运用),感觉就能较为轻松的看懂

Mobile phones-poj 1195icon-default.png?t=N7T8http://poj.org/problem?id=1195题意要求:

   给一个矩阵,初始化为全0。有以下操作:
(1)将 (x,y) 元素加 a(点修)
(2)求矩阵 [(x1,y1),(x2,y2)] 所有元素的和(区查)

考虑使用线段树,但是发现这里是二维空间,相对的,我们考虑维护一个二维线段树,一维维护行(外层),一维维护列(内层)。

学习笔记:线段树套线段树(二维线段树)_第1张图片

图片来源于董晓的博客,思路很清晰,稍微自己再理解理解就好

(主要是由图解就很会助于自己的理解)传送门icon-default.png?t=N7T8https://www.cnblogs.com/dx123/p/17903781.html

如何维护某个点:外层经过的点均入内,内层经过的点均修改

如何区查:外层覆盖即入内,内层覆盖均返回

这里在经过的每一个位置都要入内,可以这么理解:先不要管列,只看x1,x2(只看行),把它当作一个区间,然后看作一个线段树进行维护,这样一来,因为有范围更大的区间都包含这个点,且又维护的区间和,所以就以维护区间和的方式,从大到小都要更新,只不过是别的地方是pushup,这里直接从上往下的时候就更新好了。列也是这个道理,固定完某几行后,不看行,只看列即可,这样就可以理解树套树的大概含义了。(或许可以看成没有懒标记的变种,点修改)

代码如下(摘):

#include 
#include 
#include 
using namespace std;

#define N 1050
#define uls u<<1
#define urs u<<1|1
#define vls v<<1
#define vrs v<<1|1
#define mid ((l+r)>>1)
int n,sum[N<<2][N<<2]; //节点区间和

void changeY(int u,int v,int l,int r,int y,int a){ //内修
  sum[u][v]+=a; //内层经过的节点均修改,先修改
  if(l==r) return;
  if(y<=mid) changeY(u,vls,l,mid,y,a);
  else changeY(u,vrs,mid+1,r,y,a);
}
void changeX(int u,int l,int r,int x,int y,int a){ //外修
  changeY(u,1,1,n,y,a); //外层经过的节点均入内
  if(l==r) return;
  if(x<=mid) changeX(uls,l,mid,x,y,a);
  else changeX(urs,mid+1,r,x,y,a);
}
int queryY(int u,int v,int l,int r,int y1,int y2){ //内查
  if(y1<=l&&r<=y2) return sum[u][v]; //内层覆盖即返回
  if(y2<=mid) return queryY(u,vls,l,mid,y1,y2);
  else if(y1>mid) return queryY(u,vrs,mid+1,r,y1,y2);
  else return queryY(u,vls,l,mid,y1,mid)
              +queryY(u,vrs,mid+1,r,mid+1,y2);
  //这里是董晓老师的方法,当然如果常规设一个ans然后求和也是一样的
}
int queryX(int u,int l,int r,int x1,int x2,int y1,int y2){ //外查
  if(x1<=l&&r<=x2) return queryY(u,1,1,n,y1,y2); //外层覆盖即入内
  if(x2<=mid) return queryX(uls,l,mid,x1,x2,y1,y2);
  else if(x1>mid) return queryX(urs,mid+1,r,x1,x2,y1,y2);
  else return queryX(uls,l,mid,x1,mid,y1,y2)
              +queryX(urs,mid+1,r,mid+1,x2,y1,y2);
}
int main(){
  int op,x,y,a,x1,x2,y1,y2;
  while(~scanf("%d",&op)){
    if(op==0) scanf("%d",&n),
              memset(sum,0,sizeof(sum));//初始化
    if(op==1) scanf("%d%d%d",&x,&y,&a),
              changeX(1,1,n,x+1,y+1,a);
    if(op==2) scanf("%d%d%d%d",&x1,&y1,&x2,&y2),
              printf("%d\n",queryX(1,1,n,x1+1,x2+1,y1+1,y2+1));
    if(op==3) break;//3就结束
  }
  return 0;
}

你可能感兴趣的:(学习,笔记)