中压缩数组_树状数组拓展应用

在前面我们学习到,树状数组是求带修改操作的区间和的一大利器。通过预处理树状数组s[i],他表示以i结尾,长度为lowbit(i)的区间和。当需要求任意前缀和pre[x]时,都可以通过不超过logn个s数组元素(s[x],s[x-lowbit(x)],……)拼起来。当修改某个原始数组元素时,这时需要及时更新s数组,更新也不超过logn个元素(s[x],s[x+lowbit(x)],……).这样无论是修改原数组某个元素的值、还是查询前缀和都有不错的时间效率。注意数组元素下标不能为0。

下面我们再结合差分,把树状数组的应用拓展开来。

应用1:区间修改,单点查询

a6131ad782b465a83dc5927f7a1b11b7.png

解决方案:通过前面学习差分数组,我们知道原数组可以被其查分数组来代表。这样当对原数组进行区间修改时,等价于对差分数组区间端点修改,大大提高时间效率。当需要求原数组某个元素时,只需要对差分数组求一个前缀和就可以。问题转化为树状数组的最基本应用,单点修改和区间查询。

应用2:区间修改,区间查询(loj132)

f3ef517eef520d99710e21f32df210f5.png

79c699295434001ac1004ba75f8f6a00.png

解决方案:这个类型与上一个类型只有一个区别,上一个是求修改后某一个数组元素的值,而这一个是求修改后连续几个元素的值(区间)。

上一题中问题对象转化为差分数组  d1,d2,d3,d4,d5,…,dn

所以原数组元素ai=d1+d2+…+di

原数组a1+a2+…+ai的前缀和sum[i]是多少呢?

a1=d1

a2=d1+d2

a3=d1+d2+d3

ai=d1+d2+d3+…+di

每个差分元素dx在前缀和中贡献多少呢?他对原数组中第i个往后的都有贡献,所以总贡献是(i-x+1)*dx。所以sum[i]=∑(i-x+1)*dx,x∈[1,i],式子展开得到sum[i]=(i+1)∑dx-∑x*dx,x∈[1,i]。式子中包含了dx的前缀和,以及x*dx的前缀和,相当于与两个差分数组有关系。树状数组又有了用武之地。所以我们需要维护两个树状数组分别维护dx和x*dx的前缀和。

应用31:平面上单点修改,区间查询

题目描述

给出一个n*m的零矩阵 A,你需要完成如下操作:

1 x y k:表示第x行第y列元素Axy自增k ;

2 a b:表示询问第a列到第b列的所有数的和。

输入格式

输入的第一行有两个正整数 n和m;接下来若干行,每行一个操作,直到文件结束。

输出格式

对于每个 2 操作,输出一个整数,表示对于这个操作的回答。

样例

输入

2 2

1 1 1 3

1 2 2 4

2 1 1

输出

3

解决方案:

   本题目标是求连续列的元素和(包括所有行),所以修改时(求和时)只关心元素处于那一列,不关心在哪一行,那么可以把同一列压缩成一个元素,变成一维数组的单点修改区间查询问题。

本题继续拓展:

应用32:平面上单点修改,区间查询(洛谷2163)

题目描述

给出一个n*m的零矩阵 A,你需要完成如下操作:

1 x y k:表示第x行第y列元素Axy自增k ;

2 a b c d:表示询问左上角为(a,b) ,右下角为(c,d)的子矩阵内所有数的和。

注意:所有的询问操作都在修改操作之后

输入格式

输入的第一行有两个正整数 n和m;接下来若干行,每行一个操作,直到文件结束。

输出格式

对于每个 2 操作,输出一个整数,表示对于这个操作的回答。

样例

输入

2 2

1 1 1 3

1 2 2 4

2 1 1 2 2

输出

7

解决方案:

f57d0a3dec61d9f20c3c93c33331a507.png

矩形左上角(a,b)到右下角(c,d)的数字和可以归结为四个二维平面的前缀和的容斥:sum[c,d]-sum[c,b-1]-sum[a-1,b]+sum[a-1,b-1]。

如果给每个矩形编个序号,并且存储对应的四个前缀和属于哪个矩形、前面的系数(是+1还是-1)。则所有的矩形查询操作变成单点查询,查询一个点的二维前缀和。

如果对所有的点按照行坐标从小到大排序(插入和查询的点都参与),行坐标相同按照列坐标从小到大排序,如果插入点和查询点坐标相同则插入的点在前。那么后面点的二维平面前缀和从行坐标来看会包含前面点的行坐标。所以同一列的元素可以压缩成一列,变成一维数组的单点修改,区间查询问题。

应用4:平面上区间修改,单点查询

题目描述

给出一个n*m的零矩阵 A,你需要完成如下操作:

1 a b c d  k:表示左上角为(a,b) ,右下角为(c,d)  的子矩阵内所有数都自增 k;

2 x y:表示询问元素A(x,y)的值;

注意循环操作都在修改操作之后。且查询的点的x坐标是不降序列

输入格式

输入的第一行有两个正整数n,m ;接下来若干行,每行一个操作,直到文件结束。

输出格式

对于每个 2 操作,输出一个整数,表示对于这个操作的回答。

样例

输入复制

2 2

1 1 1 2 2 5

1 1 2 2 2 -3

2 1 1

2 1 2

2 2 1

2 2 2

输出复制

5

2

5

2

解决方案:

根据二维差分,平面修改可以对应为二维差分的四个单点修改。查询操作对应为二维差分数组的前缀和。因为查询操作的x逐步增大,所以同一列的元素可以压缩成一列,变成一维数组的单点修改,区间查询问题。

应用5:平面上区间修改,区间查询(洛谷p3246)

题目描述

给定一个大小为n*m的零矩阵,直到输入文件结束,你需要进行若干个操作,操作有两类:

1 a b c d x,表示将左上角为(a,b) ,右下角为(c,d) 的子矩阵全部加上x ;

2 a b c d,表示询问左上角为(a,b) ,右下角为(c,d)为顶点的子矩阵的所有数字之和。

注意所有的查询操作都在修改操作后面

输入格式

第一行两个正整数n,m,分别表示矩阵的行数与列数。

接下来若干行直到文件结束,均代表你需要进行的操作。

输出格式

对于每个 2 操作,输出一行代表查询的结果。

样例

输入

4 4

1 1 1 3 3 2

1 2 2 4 4 1

2 2 2 3 3

输出

12

解决方案:

区间修改操作可以用二维差分解决。

原数组一个元素等价于差分数组的一个前缀和。我们进一步观察原数组的前缀和与差分数组元素的关系。

A[1,1]=d[1,1]

A[1,2]=d[1,1]+d[1,2]

A[1,3]=d[1,1]+d[1,2]+d[1,3]

A[2,1]=d[1,1]+

      d[2,1]

A[2,2]=d[1,1]+d[1,2]+

      d[2,1]+d[2,2]

A[2,3]=d[1,1]+d[1,2]+d[1,3]+

      d[2,1]+d[2,2]+d[2,3]

设原数组sum[2,3]=A[1,1]+A[1,2]+A[1,3]+

                 A[2,1]+A[2,2]+A[2,3]

每个d[x][y]分别贡献了多少呢?请寻找规律。

现在假设要求sum[i][j],每个d[x][y]又分别贡献了多少呢?

每个d[x][y]对他右下角的数组元素都有影响,所以贡献了(i+1-x)*(j+1-y)*d[x][y]

因此sum[i][j]=∑(i+1-x)*(j+1-y)*d[x][y],x∈[1,i],y∈[1,j]

展开一下:sum[i][j]=(i+1)*(j+1)*∑d[x][y]-(i+1)*∑y*d[x][y]-(j+1)∑x*d[x][y]+∑x*y*d[x][y],x∈[1,i],y∈[1,j]。

所以原始数组的前缀和,与差分数组的前缀和有关系,与y*d[x][y]的前缀和有关系,与x*d[x][y]的前缀和有关系,与x*y*d[x][y]的前缀和有关系,相当于与4个差分数组有关系。

对于平面查询怎么处理呢?可以仿照“应用32”进行处理,把平面变成四个前缀和的容斥。并且把所有点的横坐标升序排列,这样前缀和都是包含关系,同一列可以压缩成一个元素,变成一维数组的单点修改和前缀和查询。

具体实现可以参考洛谷p3246的树状数组解法题解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值