维度探索——二维线段树
来源:互联网 发布:有经商软件吗 编辑:程序博客网 时间:2024/05/18 01:38
前言
线段树是一个神奇的东西,可以O(n)建树,O(logn)修改、查询,维护一个区间的性质。但是线段树维护的序列一定是一维的,如果我要维护一个“二维”的结构呢?就比如说,维护一个矩阵中子矩阵的和。简单地说就是给你一个表格,每次用“圈出”一个矩形的部分让你求它所有元素的和。
没有学过线段树的同学们一定要先学一下线段树一定要先学习一下,再来看这篇博客。
学习链接: 我与线段树的故事(纯新手请进)
1.静态二维子矩阵和
解决这个问题自然要从静态(一维)区间和得出灵感。静态一维区间和我们用的方法是求“前缀和”,我们可以用O(n)的时间复杂度求出一个pre数组,pre[i]表示闭区间[1,i]对应元素的和。如果我想要求区间[i,j]的区间和,用计算pre[j]-pre[i-1]就可以O(1)解决问题。
同学们可以尝试着把这个理论推广到二维,我们可以去维护一个二维“前缀和”来解决这个问题。
区域“I”的矩阵和,就相当于是区域“I+II+III+IV”的和减去区域“III+IV”、减去区域“III+II”、再加上区域“III”。
如果我们用sum(i,j,k,l)表示区域“I”,那么就有sum(i,j,k,l)=sum(1,j,1,l)-sum(1,i,1,l)-sum(1,j,1,k)+sum(1,i,1,k)。这样我们就把所有的数据表示成了一个“二维前缀”的形式了。我们可以用pre(i,j)表示sum(1,i,1,j),就有sum(i,j,k,l)=pre(j,l)-pre(i,l)-pre(j,k)+pre(i,k)。
pre(i,j)如何求解呢?显然可以使用和sum同样的方法:pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+a[i][j](a表示原数组)。
请看伪代码:
Init:For i = 1 to n For j = 1 to m pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+a[i][j]Query:sum(i,j,k,l)=pre[j][l]-pre[i][l]-pre[j][k]+pre[i][k]
2.二维线段树
二维线段树,每一个节点对应一个子矩阵(根节点代表整体),每个节点有四个儿子节点,分别表示它的“左上,左下,右上,右下”四个部分,例如下图:
当然,如果边长不是二的整数次幂也是可以这样二分的:
这样我们就得到了一个建树的方法:
build(Root,Left,Right,Up,Down) if 确定到唯一元素 Root.sum=这个元素 else if 这个区域只有一列 Root.左上子=新结点 Root.左下子=新结点 int mid=(Up+Down)/2 build(Root.左上子,Left,Right,Up,mid) build(Root.左下子,Left,Right,mid+1,Down) Root.sum=Root.左上子.sum+Root.左下子.sum else if 这个区间只有一行 Root.左上子=新结点 Root.右上子=新结点 int mid=(Left+Right)/2 build(Root.左上子,Left,mid,Up,Down) build(Root.右上子,mid+1,Right,Up,Down) Root.sum=Root.左上子.sum+Root.右上子.sum else Root.左上子=新结点 Root.左下子=新结点 Root.右上子=新结点 Root.右下子=新结点 int midLR=(Left+Right)/2 int midUD=(Up+Down)/2 build(Root.左上子,Left,midLR,Up,midUD) build(Root.左下子,Left,midLR,midUD+1,Down) build(Root.右上子,midLR+1,Right,Up,midUD) build(Root.右下子,midLR+1,Right,MidUD+1,Down) Root.sum=Root.左上子.sum+Root.左下子.sum+Root.右上子.sum+Root.右下子.sum
查询还是比较简单的(因为没有lazy,不懂的回去复习线段树!):
Query(Root,Left,Right,Up,Down) if Root == NULL//这样当查询NULL结点时可以直接忽略掉,不会RE return 0 return Query(Root.左上子,Left,Right,Up,Down)+ Query(Root.左下子,Left,Right,Up,Down)+ Query(Root.右上子,Left,Right,Up,Down)+ Query(Root.右下子,Left,Right,Up,Down)
然后是正经的代码:
struct NODE{ int l,r,u,d; int luch,ruch,ldch,rdch; int sum; NODE(int L=0,int R=0,int U=0,int D=0, int LUCH=0,int RUCH=0,int LDCH=0,int RDCH=0, int SUM=0){ l=L;r=R;u=U;d=D; luch=LUCH;ruch=RUCH;ldch=LDCH;rdch=RDCH; sum=SUM; }}ns[1048576];int newnode=1;//当前亟待申请的节点#define LST(ROOT) (ns[ROOT].l)#define RST(ROOT) (ns[ROOT].r)#define UST(ROOT) (ns[ROOT].u)#define DST(ROOT) (ns[ROOT].d)//表示一个结点的区间范围#define LUCH(ROOT) (ns[ROOT].luch)#define RUCH(ROOT) (ns[ROOT].ruch)#define LDCH(ROOT) (ns[ROOT].ldch)#define RDCH(ROOT) (ns[ROOT].rdch)//表示一个结点的四个儿子#define SUM(ROOT) (ns[ROOT].sum)//这些define可以使代码更好理解,但实际上没什么必要int a[101][101];void build(int root,int l,int r,int u,int d){ if(l==r && u==d) ns[root]=NODE(l,r,u,d,-1,-1,-1,-1,a[u][l]); else if(u==d) { int nlu=newnode++; int nru=newnode++; int mid=(l+r)/2; build(nlu,l,mid,u,d); build(nru,mid+1,r,u,d); ns[root]=NODE(l,r,u,d,nlu,nru,-1,-1,SUM(nlu)+SUM(nru)); }else if(l==r) { int nlu=newnode++; int nld=newnode++; int mid=(u+d)/2; build(nlu,l,r,u,mid); build(nld,l,r,mid+1,d); ns[root]=NODE(l,r,u,d,nlu,-1,nld,-1,SUM(nlu)+SUM(nld)); }else{ int nlu=newnode++; int nru=newnode++; int nld=newnode++; int nrd=newnode++; int midlr=(l+r)/2; int midud=(u+d)/2; build(nlu,l,midlr,u,midud); build(nru,midlr+1,r,u,midud); build(nld,l,midlr,midud+1,d); build(nrd,midlr+1,r,midud+1,d); ns[root]=NODE(l,r,u,d,nlu,nru,nld,nrd,SUM(nlu)+SUM(nru)+SUM(nld)+SUM(nrd)); }}int ask(int root,int l,int r,int u,int d){ if(root==-1) return 0; if( (l<=LST(root) && RST(root)<=r) && (u<=UST(root) && DST(root)<=d)) return SUM(root); if( (LST(root)>r || RST(root)<l) && (UST(root)>d || DST(root)<u)) return 0; int nlu=LUCH(root); int nru=RUCH(root); int nld=LDCH(root); int nrd=RDCH(root); return ask(nlu,l,r,u,d)+ask(nru,l,r,u,d)+ask(nld,l,r,u,d)+ask(nrd,l,r,u,d);}
理论上来讲,代码与普通线段树是极其相似的。
后记
赶稿匆忙,如有谬误,望同学们谅解。
- 维度探索——二维线段树
- HDU 4819 —— Mosaic(二维线段树)
- Uva 11297.Census——二维线段树
- 【UVA11992】Fast Matrix Operations——二维线段树
- POJ 2155——Matrix(树套树,二维树状数组,二维线段树)
- 二维线段树
- poj1656----二维线段树
- poj2155 二维线段树
- hdu1823 二维线段树
- POJ2155 二维线段树
- hdu1823 二维线段树
- HDU1823(二维线段树)
- poj2155 二维线段树
- CUGBACM_Summer_Tranning2【二维线段树】
- 二维线段树
- hdu1823 二维线段树
- hdu4819 二维线段树
- 二维线段树模版
- 联结方式
- 左边添加项到右边的JS实现
- Ansible-安装 (Centos7 通过Yum安装最新发布版本)
- Mysql总结
- C# 获取控件在客户区的坐标,相对于顶级父容器的坐标
- 维度探索——二维线段树
- UCML页面生成后突然不能访问 参数化查询 '(@ActivityID nvarchar(4000))SELECT ActivityInfoEx.ActivityInfoEx' 需要参数 '@Acti
- 通过ApplicationContextAware获取bean
- 使用AspectJ的AOP配置管理事务配置
- 解决macos在睡眠后蓝牙鼠标断开无法连接的问题
- JDOM、DOM4j读取XML文件(SAXReader)
- javascriptcore.framework的坑
- 使用HttpSessionListener接口监听Session的创建和失效
- 欢迎使用CSDN-markdown编辑器