前缀和与差分
对于一个数组a定义数组为a数组的前缀和数组。
//为了避免数组越位,下标从1开始用 for(int i=1;i<=n;++i) { s[i]=s[i-1]+a[i]; }
定义数组为a数组的差分数组。
//为了避免数组越位,下标从1开始用 for(int i=n;i;--i) //倒着for的可以不借助两个数组,这里用d和a是为了看着清楚,实际可以用一个数组储存。 { d[i]=a[i]-a[i-1]; }
前缀和与差分运算为互逆运算,任意一个数组a的前缀和数组的差分数组是它本身。
静态数组的区间求和问题
int sum(int l,int r) { return s[r]-s[l]; }
进行m次区间修改后的静态单点求值问题
假设需要操作的数组是数组a长度为8,一开始全为0。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
d | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
a | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
d | 0 | 5 | 0 | 0 | 0 | -5 | 0 |
a | 0 | 5 | 5 | 5 | 5 | 0 | 0 |
因为差分数组的前缀和数组为原数组,所以对差分数组的修改,在原数组上产生的影响是这个位置以后的一个后缀影响。
给d[l]加上x就相当于给a[l],a[l+1],a[l+2]....a[n]全部加上x。
给d[r+1]加上-x就相当于给a[r+1],r[r+2],....a[n]全部加上-x。
那么如果要给a[l],a[l+1]...a[r]全部加上x就很简单了。
void add(int l,int r,int x) { d[l]+=x; d[r+1]-=x; }注意我们操作的是数组d,是差分数组,不是原数组。也就是说如果你最后要输出原数组a的话还要在做一遍前缀和还原。
前缀和的前缀和
在上述求值问题中如果最后不是查询单点的值,而是查询区间和,那么就需要再求一遍前缀和。
第一次前缀和是为了将差分数组还原回原数组,而第二次前缀和是为了求前缀和数组。
对于一个数组a,它的差分数组d与前缀和数组s之间也是有关系的,数组s是数组d前缀和的前缀和,而数组d是数组s的二阶差分。
如果需要动态维护前缀和的前缀和,可参考
二维前缀和与差分
高维前缀和(SOSDP)
动态前缀和
线段树: