前缀和 差分

本文详细介绍了如何在线获取一维和二维数组的前缀和与差分数组,以及它们在区间修改问题中的应用,包括修改和查询操作的复杂度分析。
摘要由CSDN通过智能技术生成

前缀和 差分

一种预处理技巧,可在获取数组元素时在线获取前缀和/差分数组,之后要用的时候即可用 O ( 1 ) O(1) O(1)的复杂度进行访问。

前缀和数组可在 O ( 1 ) O(1) O(1)​的复杂度访问前 n n n项元素之和,差分是前缀和的逆运算,适合用于区间修改(先修改后查询)的问题。

前缀和、差分间的关系: d 差分 ⟶ ∑ a 原 ⟶ ∑ s 前缀和 \underset{差分}{d}\stackrel{\sum}{\longrightarrow}\underset{原}{a}\stackrel{\sum}{\longrightarrow}\underset{前缀和}{s} 差分da前缀和s

一般定义前缀和、差分数组时,通常选择浪费一个空间,以提升编码速度(无需考虑下标为负的情况)

一维前缀和 一维差分

  • 一维前缀和: s [ i ] = s [ i − 1 ] + a [ i ] s[i]=s[i-1]+a[i] s[i]=s[i1]+a[i]

  • 一维差分: d [ i ] = a [ i ] − a [ i − 1 ] d[i]=a[i]-a[i-1] d[i]=a[i]a[i1]

    区间修改:在差分数组中,​​对 d [ j ] d[j] d[j]进行修改后,相当于在原数组中修改了 [ j , + ∞ ] [j,+\infty] [j,+]范围内的值。若只在 [ j , k ] [j,k] [j,k]上进行区间修改,需在 d [ k + 1 ] d[k+1] d[k+1]处撤销修改(加上在第 d [ j ] d[j] d[j]处修改值的相反数)。

已在其他处获取 m a x , a 数组 , l 1 , r 1 , x , l 2 , r 2 max,a数组,l1,r1,x,l2,r2 max,a数组,l1,r1,x,l2,r2的数据。

extern int max,d[max+1],a[max+1],s[max+1];//max:数组最大长度 a:原数组 d:差分数组 s:前缀和数组
extern int l1,r1,x,l2,r2;//l:修改左区间 r:修改右区间 x:修改增量 l2:查询左区间 r2:查询右区间
  • s和d数组元素获取
for(int i=1;i<=max;i++){//获取d和s数组可在获取a数组时在线获取
    d[i]=a[i]-a[i-1];//获取差分数组
    s[i]=s[i-1]+a[i];//获取前缀和数组
}
  • 区间修改
d[l1]+=x;//在左区间处进行修改
d[r1+1]-=x;//在(右区间+1)处撤销修改
  • 区间查询
for(int i=1;i<=max;i++){
    a[i]=d[i]+a[i-1];//求出修改后的原数组
    s[i]=a[i]+s[i-1];//求出修改后的前缀和数组
}
int ans=s[r2]-s[l2-1];//区间查询前缀和

二维前缀和 二维差分

  • 二维前缀和: s [ j ] [ i ] = s [ j − 1 ] [ i ] 上方所有部分 + s [ j ] [ i − 1 ] 左方所有部分 − s [ j − 1 ] [ i − 1 ] 左上被加 2 次部分 + a [ j ] [ i ] 本身 s[j][i]=\underset{上方所有部分}{s[j-1][i]}+\underset{左方所有部分}{s[j][i-1]}-\underset{左上被加2次部分}{s[j-1][i-1]}+\underset{本身}{a[j][i]} s[j][i]=上方所有部分s[j1][i]+左方所有部分s[j][i1]左上被加2次部分s[j1][i1]+本身a[j][i]

    二维矩形区间内的前缀和:如:从 [ y , x ] [y,x] [y,x] [ j , i ] [j,i] [j,i]二维矩形范围内的前缀和: s = s [ j ] [ i ] − s [ y − 1 ] [ i ] 上方所有部分 − s [ j ] [ x − 1 ] 左方所有部分 + s [ y − 1 ] [ x − 1 ] 左上方被减 2 次部分 s=s[j][i]-\underset{上方所有部分}{s[y-1][i]}-\underset{左方所有部分}{s[j][x-1]}+\underset{左上方被减2次部分}{s[y-1][x-1]} s=s[j][i]上方所有部分s[y1][i]左方所有部分s[j][x1]+左上方被减2次部分s[y1][x1]

  • 二维差分:

    朴素方法(绝对法): d [ j ] [ i ] = a [ j ] [ i ] − a [ j − 1 ] [ i ] 上方所有部分 − a [ j ] [ i − 1 ] 左方所有部分 + a [ j − 1 ] [ i − 1 ] 左上方被减 2 次部分 d[j][i]=a[j][i]-\underset{上方所有部分}{a[j-1][i]}-\underset{左方所有部分}{a[j][i-1]}+\underset{左上方被减2次部分}{a[j-1][i-1]} d[j][i]=a[j][i]上方所有部分a[j1][i]左方所有部分a[j][i1]+左上方被减2次部分a[j1][i1]

    等效大小为1的区间修改的获取方法(相对法):在n*m的矩阵中,d数组元素初始全为0,进行 ∑ j = 1 n ∑ i = 1 m \sum\limits_{j=1}^{n}\sum\limits_{i=1}^{m} j=1ni=1m循环:

    • d [ j ] [ i ] + = a [ j ] [ i ] d[j][i]+=a[j][i] d[j][i]+=a[j][i]

    • d [ j + 1 ] [ i ] − = a [ j ] [ i ] d[j+1][i]-=a[j][i] d[j+1][i]=a[j][i]

    • d [ j ] [ i + 1 ] − = a [ j ] [ i ] d[j][i+1]-=a[j][i] d[j][i+1]=a[j][i]

    • d [ j + 1 ] [ i + 1 ] + = a [ j ] [ i ] d[j+1][i+1]+=a[j][i] d[j+1][i+1]+=a[j][i]

    区间修改:对 d [ y ] [ x ] d[y][x] d[y][x]进行修改后,在原数组中修改的是 [ y , x ] [y,x] [y,x] [ + ∞ , + ∞ ] [+\infty,+\infty] [+,+]范围矩形内的值。如,在 [ y , x ] [y,x] [y,x] [ j , i ] [j,i] [j,i]范围矩形内中每个元素加 x x x,需:

    • d [ y ] [ x ] + = x d[y][x]+=x d[y][x]+=x:原数组 [ y , x ] [y,x] [y,x] [ + ∞ , + ∞ ] [+\infty,+\infty] [+,+]矩形范围中元素 + x +x +x
    • d [ j + 1 ] [ x ] − = x d[j+1][x]-=x d[j+1][x]=x:原数组 [ j + 1 , x ] [j+1,x] [j+1,x] [ + ∞ , + ∞ ] [+\infty,+\infty] [+,+]矩形范围中元素 − x -x x(位于区间修改矩形下方)
    • d [ y ] [ i + 1 ] − = x d[y][i+1]-=x d[y][i+1]=x:原数组 [ y , i + 1 ] [y,i+1] [y,i+1] [ + ∞ , + ∞ ] [+\infty,+\infty] [+,+]矩形范围中元素 − x -x x(位于区间修改矩形右方)
    • d [ j + 1 ] [ x + 1 ] + = x d[j+1][x+1]+=x d[j+1][x+1]+=x:原数组 [ j + 1 , x + 1 ] [j+1,x+1] [j+1,x+1] [ + ∞ , + ∞ ] [+\infty,+\infty] [+,+]矩形范围中元素被减了2次,撤销此修改(位于区间修改矩形的右下方)

已在其他处获取 m , n , a 数组 , _ x 1 , _ x 2 , _ y 1 , _ y 2 m,n,a数组,\_x1,\_x2,\_y1,\_y2 m,n,a数组,_x1,_x2,_y1,_y2数据

extern int m,n,x1,y1,x2,y2,d[n+1][m+1],a[n+1][m+1],s[n+1][m+1];
//n,m:n*m矩阵的矩阵维度 x1,y1:区间修改起点坐标 x2,y2:区间修改终点坐标 d:差分数组 a:原数组 s:前缀和数组
  • s和d数组元素的获取
for(int j=1;j<=n;j++){
    for(int i=1;i<=m;i++){
        s[j][i]=s[j-1][i]+s[j][i-1]-s[j-1][i-1]+a[j][i];//获取二维前缀和数组
    }
}
for(int j=1;j<=n;j++){
    for(int i=1;i<=m;i++){
		d[j][i]=a[j][i]-a[j-1][i]-a[j][i-1]+a[j-1][i-1];//获取二维差分数组(朴素法)
    }
}
//获取二维差分数组(相对法)
for(int j=1;j<=n;j++){
    for(int i=1;i<=m;i++){
        d[j][i]+=a[j][i];
        d[j+1][i]-=a[j][i];
        d[j][i+1]-=a[j][i];
        d[j+1][i+1]+=a[j][i];
    }
}
  • 区间查询
int ans=s[y2][x2]-s[y1-1][x2]-s[y2][x1-1]+s[y1][x1];
  • 区间修改
d[y1][x1]+=x;
d[y1][x2+1]-=x;
d[y2+1][x1]-=x;
d[y2+1][x2+1]+=x;
for(int j=1;j<=n;j++){
    for(int i=1;i<=m;i++){
        a[j][i]=a[j-1][i]+a[j][i-1]-a[j-1][i-1]+d[j][i];//重新计算原数组
        s[j][i]=s[j-1][i]+s[j][i-1]-s[j-1][i-1]+a[j][i];//重新计算前缀和数组
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值