专栏文章
算法笔记:前缀和 & 差分
算法·理论参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minwy2cx
- 此快照首次捕获于
- 2025/12/02 09:42 3 个月前
- 此快照最后确认于
- 2025/12/02 09:42 3 个月前
前缀和
适用范围:区间求和。
一维
例 有一个长度为 的数列 ,有 次询问,每次询问 ,,要求输出 。
首先考虑暴力,每次询问时:
CPPint s=0;
for(int i=l;i<=r;i++)s+=a[i];
cout<<s<<'\n';
时间复杂度分析:最劣时,每次都 ,则时间复杂度为 。
那么我们怎么优化呢?
我们可以再考虑一个数组 存储 的前缀和,即
稍作变形便可得出递推式:
那么这样有什么用呢?
注意到,,这样便将 的遍历变成了 的计算,总复杂度直接降为 。
例 参考代码
CPP#include<bits/stdc++.h>
#define int long long
#define N 10000005//本处以n<=1e7举例
using namespace std;
int a[N],s[N],n,q;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++){cin>>a[i];s[i]=s[i-1]+a[i];}
while(q--){
int l,r;cin>>l>>r;
cout<<s[r]-s[l-1]<<'\n';
}
return 0;
}
二维
例 有一个 行 列的二维数组 ,有 次询问,每次询问 ,求 。
本题与上一例极为类似,只是改成了二维。
先考虑暴力,每次 的遍历数组,总时间复杂度 。
类似地,设 ,易得递推式为:
可以参考图来理解(绿色部分为黄蓝的重叠处):

预处理好 后,每次询问时,类似预处理的推导,易得:
复杂度优化至 。
差分
适用范围:区间整体加减。
一维
例 有一个长度为 的数列 ,有 次操作,每次输入 ,,,要求对于每一个 ,都将 的值加上 ,最后输出 。
暴力做法为 ,可以使用差分进行优化。
定义差分数组 ,其中
为什么要把差分和前缀和放在一起呢?
我们对差分数组求前缀和,设其为 :
于是我们得到一个重要的结论:差分数组的前缀和数组为原数组。
类似的,还可以得到:前缀和数组的差分数组为原数组。
所以:前缀和和差分互为逆运算。
回到例题,对于一次操作,设操作后的原数组和差分数组分别为 和 。
分类讨论:
- 对于任意的 ,。
- 对于 ,。
- 对于 ,。
综上所述,对于 的区间整体加 的修改,仅需对差分数组 进行如下修改:
CPPd[l]+=w;
d[r+1]-=w;
次操作后,只需对 求前缀和并输出即可。
例 参考代码
CPP#include<bits/stdc++.h>
#define int long long
#define N 10000005//本处以n<=1e7举例
using namespace std;
int a[N],d[N],n,q;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++){cin>>a[i];d[i]=a[i]-a[i-1];}
while(q--){
int l,r,w;cin>>l>>r>>w;
d[l]+=w;
d[r+1]-=w;
}
for(int i=1;i<=n;i++){
a[i]=a[i-1]+d[i];
cout<<a[i]<<' ';
}
return 0;
}
时间复杂度:。
二维
例 有一个 行 列的二维数组 ,有 次操作,每次操作输入 ,对于任意的 和 ,将 加上 ,最后输出 。
我们已经知道在一维中,差分和前缀和互为逆运算。类比过来,设差分数组 :
对照一维前缀和及差分的迁移,不难想出二维的迁移,这里不写推导过程,作为思考题留给读者,直接给出操作代码:
CPPd[x_1][y_1]+=w;
d[x_2+1][y_1]-=w;
d[x_1][y_2+1]-=w;
d[x_2+1][y_2+1]+=w;
最后对差分数组求前缀和即可。
时间复杂度:。
课后作业
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...