【bzoj3132】上帝造题的七分钟 二维树状数组区间修改区间查询
题目描述
“第一分钟,X说,要有矩阵,于是便有了一个里面写满了0的n×m矩阵。
第二分钟,L说,要能修改,于是便有了将左上角为(a,b),右下角为(c,d)的一个矩形区域内的全部数字加上一个值的操作。
第三分钟,k说,要能查询,于是便有了求给定矩形区域内的全部数字和的操作。
第四分钟,彩虹喵说,要基于二叉树的数据结构,于是便有了数据范围。
第五分钟,和雪说,要有耐心,于是便有了时间限制。
第六分钟,吃钢琴男说,要省点事,于是便有了保证运算过程中及最终结果均不超过32位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。”
——《上帝造裸题的七分钟》
所以这个神圣的任务就交给你了。
输入
输入数据的第一行为X n m,代表矩阵大小为n×m。
从输入数据的第二行开始到文件尾的每一行会出现以下两种操作:
L a b c d delta —— 代表将(a,b),(c,d)为顶点的矩形区域内的所有数字加上delta。
k a b c d —— 代表求(a,b),(c,d)为顶点的矩形区域内所有数字的和。
请注意,k为小写。
输出
针对每个k操作,在单独的一行输出答案。
样例输入
X 4 4
L 1 1 3 3 2
L 2 2 4 4 1
k 2 2 3 3
样例输出
12
题解
二维树状数组区间修改区间查询
头一次知道树状数组还能实现区间修改区间查询~
我们先考虑一维的情况:树状数组的本质是O(logn)维护前缀和,前缀相减可以得到区间和。
而又有一种区间修改单点查询的方法:差分。
设d[i]=a[i]−a[i−1],那么a[i]=(a[i]−a[i−1])+(a[i−1]−a[i−2])+...+(a[1]−a[0])=n∑i=1d[i],于是可以按照相同的方法维护差分数组的前缀和,修改的时候直接将l加上,r+1减去即可。
那么对于区间查询呢?我们还是引入差分的思想,则n∑i=1a[i]=n∑i=1i∑j=1d[j]=n∑j=1(n+1−j)d[j]=(n+1)n∑j=1d[j]−n∑j=1j·d[j]。所以维护两个树状数组,一个维护d[j]的前缀和,一个维护j*d[j]的前缀和就可以了。
那么对于本题变成二维的呢?其实也是一样的。
还是使用差分的方法,a[n][m]=n∑i=1m∑j=1d[i][j],n∑i=1m∑j=1a[i][j]=n∑i=1m∑j=1i∑k=1j∑l=1d[k][l]=n∑k=1m∑l=1(n+1−k)(m+1−l)d[k][l]。
展开什么的,自己动手丰衣足食
然后开4个树状数组即可。修改和查询时使用容斥原理拆成4段来完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | #include <cstdio> #include <algorithm> #define N 2055 using namespace std; int n , m; char str[5]; struct data { int f[N][N]; void update( int x , int y , int a) { int i , j; for (i = x ; i <= n ; i += i & -i) for (j = y ; j <= m ; j += j & -j) f[i][j] += a; } int query( int x , int y) { int i , j , ans = 0; for (i = x ; i ; i -= i & -i) for (j = y ; j ; j -= j & -j) ans += f[i][j]; return ans; } }A , B , C , D; void modify( int x , int y , int z) { A.update(x , y , z) , B.update(x , y , x * z) , C.update(x , y , y * z) , D.update(x , y , x * y * z); } int solve( int x , int y) { return (x + 1) * (y + 1) * A.query(x , y) - (y + 1) * B.query(x , y) - (x + 1) * C.query(x , y) + D.query(x , y); } int main() { int x1 , y1 , x2 , y2 , z; scanf ( "%*s%d%d" , &n , &m); while (~ scanf ( "%s%d%d%d%d" , str , &x1 , &y1 , &x2 , &y2)) { if (str[0] == 'L' ) { scanf ( "%d" , &z) , x2 ++ , y2 ++ ; modify(x1 , y1 , z) , modify(x2 , y1 , -z) , modify(x1 , y2 , -z) , modify(x2 , y2 , z); } else x1 -- , y1 -- , printf ( "%d\n" , solve(x1 , y1) - solve(x2 , y1) - solve(x1 , y2) + solve(x2 , y2)); } return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步