浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此。但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行。

但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问。此时就需要根据线段树与树状数组的优缺点来选择了。

做一下基本操作的对比,如下图。

因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长。

对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制只能将其分解成n*m次单点更新。

基本操作就是这些,下面说一下怎么实现这些操作。

树状数组(此部分摘自wyl8899《树状数组维护区间和的模型及其拓广的简单总结》):

从一维的区间更新,单点查询开始讲起。

一维的时候可以建立辅助数组d[1] = a[1] , d[i] = a[i] - a[i-1],则此时a[i] = d[1] + d[2] + ......+d[i]。则,

对a[] 的单点查询就变成了d[]的前 i 项和查询;

对a[]的成段更新变成了对d[]的两次单点更新,如要在a[]的[l,r]上加上w,即为d[l] += w,d[r+1] -= w,维护一下d[]对应的树状数组就好了。

接下来是一维的区间更新,区间查询。

假设求a[]的前i项和,会有

sum = a[1] + a[2] + ......+ a[i] = i*d[1] + (i-1)*d[2] +......(i-x+1)*d[x]+......+1*d[i] = sigma(i-1)*d[x] - sigma(x*d[x]) = (i-1)*sigma(d[x]) - sigmax*d[x]   ( 1<= x && x <= i)。

所以只需要再加一个辅助数组来维护x*d[x]。

如此,则对于a[]的区间操作均转化成了对d[]和x*dx[]的单点操作。

二维的情况:

现附上单点更新及区间查询的模板。

int sum(int a[maxn][maxn],int x,int y){
  int s=0,t;
  for(;x;x-=lowbit(x))
    for(t=y;t;t-=lowbit(t))
      s+=a[x][t];
  return s;
}
void updata(int a[maxn][maxn],int x,int y,int w){
  int t;
  for(;x<=n;x+=lowbit(x))
    for(t=y;t<=m;t+=lowbit(t))
      a[x][t]+=w;
}

然后继续挖掘树状数组的性质,一个很浅显的性质。

首先一维的时候,若对a[i] += w,那么a[]的前x(1 <= x <= n)项和均会加w。

二维的时候显然也是这样,则我们用a[x][y]表示原数组A[][]从(x,y) --(n,m)的变化。

则对A[][]的单点A[x][y]的查询转化为求a[][]的(1,1)--(x,y)的和。

现在来说矩阵加减,矩阵求和,仍用a[][]表示A[][]的变化。

则A[][]的(1,1)--(x,y)的和为sum = sigma(a[i][j] *(x-i+1)*(y-j+1))(1 <= i <= x ,1 <= j <= y)。

因为a[i][j]表示A[][]从(i,j)--(n,m)的变化,而到(i,j)--(x,y)有(x-i+1)*(y-j+1)个元素,故得上述式子。

将上式展开后得(x+1)(y+1)sigma(a[i,j]) - (x+1)sigma(a[i,j]*j) - (y+1)sigma(a[i,j]*i) + sigma(a[i,j]*i*j)。

同一维,用四个辅助数组记录sigma(a[i,j]) ,sigma(a[i,j]*j), sigma(a[i,j]*i) , sigma(a[i,j]*i*j)。

具体代码见http://blog.csdn.net/zmx354/article/details/31759121

线段树:

首先是树的样子,对第一维建立一棵线段树T1,T1的每个节点均对应一棵线段树Ti2(i表示t1的节点编号),Ti2对应第二维。

然后是初始化,以求num[][]的最大值为例。

设s1,s2分别为T1,T(s1)2的节点编号。

若s1,s2均为叶子结点,则st[s1][s2] = num[x][y],以为此时st[s1][s2]对应的就是一个点。

若只有s1为叶子结点,则有st[s1][s2] =  max(st[s1][s2<<1],st[s1][s2<<1|1])。

若都不是叶子结点则有,st[s1][s2] = max(st[s1<<1],st[s2],st[s1<<1|1][s2])。

单点更新:

首先是第一维找到对应结点site,则site到root路径上所有Ti2均要更新,一共更新log(n)*log(m)。

区间更新,加两个lazy标记,第二维的就是一维线段树中的。第一维不同之处在于需要记录当前更新中第二维的范围。

更新是也要讨论s1,s2是否为叶子结点。学的时候被这里卡了好久,有一次和zp扯淡的时候突然顿悟了。

关于查询就不说了,这是最像一维的了。

以上均为弱菜的井底之见,不足之处,敬请指出。

浅谈二维中的树状数组与线段树,布布扣,bubuko.com

时间: 2024-12-05 13:07:59

浅谈二维中的树状数组与线段树的相关文章

树状数组与线段树

一:树状数组 树状数组是对一个数组改变某个元素和求和比较实用的数据结构.两中操作都是O(logn). 需求:有时候我们需要频繁地求数组的前k项和或者求数组从小标i到j的和,这样每次最坏情况下的时间复杂度就会为O(N),这样效率太低了.而树状数组主要就是为了解决这样一个问题.树状数组在求和及修改都可以在O(lgN)时间内完成. 树状数组需要额外维护一个数组,我们设为C[N],原数组为A[N], 其中每个元素C[i]表示A[i-2^k+1]到A[i]的和,这里k是i在二进制时末尾0的个数.注意通过位

HDU 1166 敌兵布阵 (我的树状数组加线段树点修改模板)

思路:本题因为是点修改,所以我们可以用线段树或者是树状数组了.线段树的基本操作我在我的代码中会具体体现,关键是要理解下面这幅图,具体的思想大家可以去看看其他的资料 线段树AC代码: #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; #define N 50005 int num

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过128MB = = 嗯就是这样,代码长度= =我写了260行......Debug了n小时= = 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #in

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin

HDU 1566 Color the ball(树状数组or线段树)

Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11387    Accepted Submission(s): 5680 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"

BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接 方法:树状数组套线段树 解析: 这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队.这道题比那道更要简单. 然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy.引用理论部分. 删除某个数,只要统计它之前还存在的比它大的

【POJ】1990-MooFest(树状数组or线段树)

树状数组和线段树的解法比较,感觉不论在内存还是时间上都是树状数组占优 大题思路对 v从小到大进行排序,之后找之前的(也就是比v小的坐标) v * sum(abs(xi - x)) 这样的话 abs无法处理,我们用另外一个树状数组记录在x ~ y区间牛的个数,之前那个记录在x ~ y区间内牛的坐标和 Accepted 484 79 C++ 1131   树状数组代码: #include<cstdio> #include<algorithm> #include<cstring&g

树状数组与线段树(二)

树状数组 1.小朋友排队 n 个小朋友站成一排. 现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友. 每个小朋友都有一个不高兴的程度. 开始的时候,所有小朋友的不高兴程度都是 0. 如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1,如果第二次要求他交换,则他的不高兴程度增加 2(即不高兴程度为 3),依次类推.当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k. 请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少. 如果有两个小朋友身高一样