前缀和、二维前缀和与差分的小总结

前缀和、二维前缀和与差分的小总结在了解二维前缀和之前 我们首先需要了解一下什么是前缀和 如果我给你一串长度为 n 的数列 a1 a2 a3 an 再给出 m 个询问 每次询问给出 L R 两个数 要求给出区间 L R 里的数的和 你会怎么做 若是没有了解过前缀和的人看到这道题的想法可能是对于 m 次询问 我每次都遍历一遍它给的区间 计算出答案 这样子的方法固然没错 但是其时间复杂度达到了 O n m 如果数据量稍微大一点就有可能超时

在了解二维前缀和之前,我们首先需要了解一下什么是前缀和。

如果我给你一串长度为n的数列a1,a2,a3……an,再给出m个询问,每次询问给出L,R两个数,要求给出区间[L,R]里的数的和,你会怎么做,若是没有了解过前缀和的人看到这道题的想法可能是对于m次询问,我每次都遍历一遍它给的区间,计算出答案,这样子的方法固然没错,但是其时间复杂度达到了O(n*m),如果数据量稍微大一点就有可能超时,而我们如果使用前缀和的方法来做的话就能够将时间复杂度降到O(n+m),大大节省了运算时间。至于怎么用,请看下面一小段代码

a[0]=0; for(int i=1;i<=n;i++)a[i]+=a[i-1];

没错,前缀和顾名思义就是前面i个数的总和。数组a在经过这样的操作之后,对于每次的询问,我们只需要计算a[R]-a[L-1]就能得到我们想要的答案了,是不是很简单呢。

在知道了最简单的前缀和之后,我们再来了解一下什么是差分。

给你一串长度为n的数列a1,a2,a3......an,要求对a[L]~a[R]进行m次操作:

操作一:将a[L]~a[R]内的元素都加上P

操作二:将a[L]~a[R]内的元素都减去P

最后再给出一个询问求a[L]-a[R]内的元素之和?

你会怎么做呢?你可能会想,我对于m次操作每次都遍历一遍a[L]~a[R],给区间里的数都加上P或减去P,最后再求一次前缀和就行了。没错,这样子确实也能得出正确答案,但时间复杂度却高达O(M*n),对于1<=n,m<=1e5这个数据范围来说直接就tle了,所以说这个方法不可行。既然这样不行的话,那我们要怎么做才能快速的得到正确答案呢?是的,这个时候我们的差分就该派上用场了,我们新开一个数组b,储存每一次的修改操作,最后求前缀和的时候统计一下就能快速的得到正确答案了,详细请看下面代码。

#include 
  
    using namespace std; const int maxn=1e5+9; int a[maxn],b[maxn]; int main(){ int i,j,k,n,m,p; cin>>n>>m; for(i=1;i<=n;i++){ cin>>a[i]; } for(i=1;i<=m;i++){ int L,R,t; cin>>t>>L>>R>>p; if(t==1){ b[L]+=p;b[R+1]-=p; //仔细想想为什么b[R+1]要减去p } else{ b[L]-=p;b[R+1]+=p; } } int add=0; for(i=1;i<=n;i++){ add+=b[i]; a[i]+=a[i-1]+add; } int x,y; cin>>x>>y; cout< 
    
  

相信看到这里,大家已经仔细思考过代码了,为什么操作一时b[R+1]要减去p,很简单,因为操作一我只需对[L,R]区间里的数加p,[R+1,n]这个区间里的数没必要加p,所以需要减掉p。

差分讲解完毕,接下来我们终于要开始今天的正题——二维前缀和了。

还是以小问题的形式来讲解二维前缀和吧。

给定一个n*m大小的矩阵a,有q次询问,每次询问给定x1,y1,x2,y2四个数,求以(x1,y1)为左上角坐标和(x2,y2)为右下角坐标的子矩阵的所有元素和。注意仍然包含左上角和右下角的元素。

怎么做呢?为了方便你们理解,我画个图吧。

前缀和、二维前缀和与差分的小总结

图画的很丑,希望不要介意。如图所示,按题目要求,我们每次要求的答案就是红色圆圈所在的区域的值(注意,这里的x1,x2表示行,y1,y2表示列),对比上面这张图我们能够发现红色区域的值等于四个区域的值减去(白色区域+黑色区域),再减去(白色区域+蓝色区域),最后因为白色区域被减了两次,我们需要再加回来。所以ans=a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];(注意,此时的a数组代表的是前缀和)。突然想起来还没说怎么求二维前缀和,很简单,看下面代码。

for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1]; } 

为方便理解贴个图

前缀和、二维前缀和与差分的小总结

假如我想求a[2][4]的前缀和,我得先加上a[1][4]的前缀和,再加上a[2][3]的前缀和,然后这个时候我们发现实际上a[1][3]这个部分我们加了两遍,所以我们需要再减去一遍a[1][3],于是得出公式a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1]

接下来看完整代码吧。

#include 
  
    using namespace std; const int maxn=1e3+9; int a[maxn][maxn]; int main(){ int i,j,k,n,m,q; cin>>n>>m>>q; for(i=1;i<=n;i++){ for(j=1;j<=m;j++) cin>>a[i][j]; } for(i=1;i<=n;i++){ for(j=1;j<=m;j++) a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1]; } for(i=1;i<=q;i++){ int x1,y1,x2,y2; cin>>x1>>y1>>x2>>y2; int ans=a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1]; cout< 
    
  

是不是感觉很简单呢,哈哈哈哈哈哈哈。

在学完二维前缀和之后,一些同学可能会有疑问,一维前缀和能用上差分,那么二维前缀和能不能用上差分呢?答案是肯定的。

那么怎么差分呢?方法是和一维类似的,我们也是需要另开一个数组记录修改操作,最后求前缀和时统计修改操作,只是二维每一次操作需要记录4个位置,一维只需要记录2个位置。具体怎么做,看下面代码吧。

for(int i=0;i 
  
    >x1>>y1>>x2>>y2>>p; b[x1][y1]+=p;b[x2+1][y2+1]+=p; b[x2+1][y1]-=p;b[x1][y2+1]-=p; } 
  

好了,一维前缀和、二维前缀和、差分都说完了,希望看这篇文章的人能够有所收获吧。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/220370.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午8:39
下一篇 2026年3月17日 下午8:40


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号