[[TOC]]
差分
设原数组的第i个元素为ai,那么差分数组的第i个元素为di=ai−ai−1
有一组数据如下
下标原数组 a[i]差分数组 d[i]差分数组d前缀和 s[i]1333252531−414767581864−44
发现:对差分数组进行前缀和操作,就可以得到原数组了
Si=j=1∑idi=d1+d2+⋯+di=(a1−a0)+(a2−a1)+⋯+(ai−ai−1)=(a1−a0)+(a2−a1)+⋯+(ai−ai−1)=ai−a0因为a0=0=ai
现在对原数组a的区间[2,4]上的每个数都增加2,得到如下
下标原数组 a[i]差分数组 d[i]差分数组d前缀和 s[i]1333274733−43496958−1864−44
发现,对原数组进行区间[2,4]修改: 差分数组只会修改两个值,第一个值为b2=b2+2,第二个值为b5=b5−2,且对修改后的差分数组进行前缀后依然得到了原数组.
于是,得出结论,如果对原 数组进行区间[l,r]上的每个数都增加v,那么对应的差分数组只需要修改两位置bl=bl+v, br+1=br+1−v.原来需要r−l+1次的区间操作,现在只需要进行2次区间操作.也就是++把区间增减转了两次的单点增减++.大大节省了时间
对于,那些题目
适合使用差分思想(把原数组转化成差分数组来操作)
思考与总结
差分是前缀和的逆运算
已知,原数组a就是数组s,两者一模一样.所以在这里我们其实是先假定数组a是数组d形成的前缀和数组,进行就想到++能否通过前缀和数组a推导出数组d呢?++
我们现在认为原数组a上每个值ai都是某个数组d的前缀和.那么可以得到
ai=ai−1+di(1)移项,进而得到
di=ai−ai−1(2)通过(2)式,把数组a转化成d数组.也就是前缀和数组转化成差分数组
可以通过a得到d,也可以通过d得到a,他们互为逆运算
a⇌d可以认为,a与d是同一事物的不同表现形式.
综上,可以得到如下的性质:
设d是原数组,a是d的前缀和,
- 在d的两个位置i,j上,同时+v,−v,也就是di=di+v,dj=dj−v,那么体现到a上,那就是区间[i,j−1]上的每个数都增加v
- 在a上的区间[i,j−1]上的每个数都增加v,那么体现到d上,那就是位置i,j,+v,−v,也就是di=di+v,dj=dj−v
函数思想
设a是一个数字组成的序列,定义函数如下:
- f(a)=b,把序列a转化成b,其中bi=∑1iai,也就是前缀和
- f−1(a)=b,表示把序列a转化成b,其中bi=ai−ai−1,也就是差分
显然f与f−1互为逆运算,也就是
f−1(f(a))=af(f−1(a))=a同样可以想到f与f−1是++双射函数++,也就是f(a)=b 也就是说ranf与domf++一一映射++
定义函数range(a,l,r),表示对序列a区间[l,r]上的每个数都增加1
range(a1,l,r)=a2表示a1经过区间[l,r]上的 每个数都增加1后得到a2,显然a1=a2,则f−1(a1)=d1=f−1(a2)=d2^[因为一一映射的性质]
根据差分的性质知d1,d2只有两个点不同,且这两个点是l,r+1
定义函数
定义函数add(a,l,r),表示对序列a上的两点l,r+1分别+1,−1
add(a1,l,r)=a2
则range(a1,l,r)与add(d1,l,r)是++一一映射++的.也就是每一个在序列a+1上的range操作,都等价于在序列d1=f−1(a1)上的add操作^[这里其时使用了反证法]
二维差分
111111−9−9−9−3−3−3555根据上面的思考,可以认为此时的二维矩阵上的值,都是每个二维矩阵d的前缀和.也就是
⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡Si,j=Si−1,j+Si,j−1−Si−1,j−1+di,j移项,得到原二维矩阵上的每个值为:
di,j=Si,j−Si−1,j−Si,j−1+Si−1,j−1对S矩阵上的子矩阵(xi,yi),(xj,yj)进行+v操作,反应到d上就是
- (xi−1,yi−1),(xj+1,yj+1)上的每个数都+v
- (xi,yj+1),(xj+1,yi)上的每个数都−v
小技巧: 点di,j的值是Si,j−Si−1,j−Si,j−1+Si−1,j−1,
也就是说,与以i,j作为左下角的四个格子的值有关.也就是说i,j的覆盖范围,有奇数个+v时才会产生影响.

题目:
练习题目
- HDU 1121
- luogu 3948
- luogu P1969 积木大赛
- P6070
- P3655
- P7404
- roj 求最少多少次把所有的元素变成一样大,平数II