Markdown代码2616字,渲染出实际1934字
前言
前缀和和差分是优化的常见手段。
本篇很水
前缀和
假设有一个数组A,现在要对Al到Ar进行求和。

假设l=2,r=4,那么求和的操作:
首先新建两个变量:ans=0用于记录和,i=l用于求和操作。

ans=0,i=2
接下来将ans加上i指向的值,并将i右移:

ans=2,i=3
继续累加ans并右移i:

ans=5,i=4
i到达r,累加ans并结束。
最终答案:ans=8
可以发现,这里的方法如果用代码实现,时间复杂度很高(O(r−l+1)),如果题目给到的数据大一点就会爆TLE。
这个时候,就可以使用前缀和来优化代码,实现O(1)的时间复杂度。
新建一个数组S,Si表示A数组前i项的值,即Si=A1+A2+⋯+Ai−1+Ai。

S1=A1
S2=A1+A2
S3=A1+A2+A3
S4=A1+A2+A3+A4
S5=A1+A2+A3+A4+A5
S6=A1+A2+A3+A4+A5+A6
依旧是l=2,r=4求区间和:
这段区间的区间和是A2+A3+A4,刚好等于S4−S1。

验证答案:S4−S1=9−1=8。答案正确!
也就是说,l到r的区间和为Sr−Sl−1。
那么如何在程序中快速生成前缀和数组呢?
我们可以注意到,Si只比Si−1多一个Ai,如:
S3=A1+A2+A3
S4=S3A1+A2+A3+A4=S3+A4
得出:Si=Si−1+Ai。
程序实现
for(int i = 1;i <= n;i++){
cin >> a[i];
s[i] = a[i-1]+s[i];
}
cin >> l >> r;
cout << s[r]-s[l-1];
一个小特性:当i=1,程序内s[1]=s[0]+a[1],如果s为全局变量则s[0]等于0,s[1]直接等于a[1]。
差分
有一个数组A,现在要将数组中从l到r区间的数全部加上t。
常规暴力实现:假设l=2,r=4,t=3,定义一个i=l。

将i指向的数加上t,并将i+1:

继续操作:

i到达r,最后一次操作i指向的元素加上t,循环结束。
结束后的数组A:

这个方法的时间复杂度依然是O(r−l+1)。
要优化这里的代码,可以使用差分。
差分适用于有多次区间加减的题目,单次加减甚至会负优化(O(t(r−l+1))到O(tn))
增加差分数组D:

其中Di=Ai−Ai−1。
对于每次操作,将Dl加上t,Dr+1减去t.
解释:增大Al的值会增大Di(Ai)−Ai−1的值,而l到r区间内的值由于一起加减,差分数组不变。
增大Ar的值会减小Dr+1(Ar+1−Ar)的值,所以减去t。
此处假设Di只有正数。
最后,给D数组做一个前缀和得到原数组A。
解释:Di=Ai−Ai−1,Ai=Di+Ai−1。
代码实现:
int n,q,d[12];
cin >> n >> q;
int tmp = 0;
for(int i = 1;i <= n;i++){
int x;cin >> x;
d[i] = x-tmp;
tmp=x;
}
while(q--){
int l,r,t;cin >> l >> r >> t;
d[l]+=t;
d[r+1]-=t;
}
tmp=0;
for(int i = 1;i <= n;i++){
int x = tmp += d[i];
cout << x << endl;
tmp=x;
}
因为差分只需要Ai和Ai−1来构造,因此不创建A,只用变量存储Ai−1即可。
结尾
到这里就结束了前缀和和差分的解释和一维实现。二维正在赶工······
有帮助,赞一个