AcWing 算法基础课 前缀和&&差分 总结(持续更新中···)

前缀和

通过预处理,可以在O(1)的时间复杂度内询问特定区间内所有元素的和

一维前缀和

原数组: a [ 1 ] a[1] a[1] a [ 2 ] a[2] a[2] a [ 3 ] a[3] a[3] a [ 4 ] a[4] a[4]···
前缀数组: s [ 1 ] s[1] s[1] s [ 2 ] s[2] s[2] s [ 3 ] s[3] s[3] s [ 4 ] s[4] s[4]···
s [ 1 ] = a [ 1 ] s[1]=a[1] s[1]=a[1]
s [ 2 ] = a [ 1 ] + a [ 2 ] s[2]=a[1]+a[2] s[2]=a[1]+a[2]
s [ 3 ] = a [ 1 ] + a [ 2 ] + a [ 3 ] s[3]=a[1]+a[2]+a[3] s[3]=a[1]+a[2]+a[3]
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ······
s [ i ] = a [ 1 ] + a [ 2 ] + a [ 3 ] + ⋅ ⋅ ⋅ + a [ i ] s[i]=a[1]+a[2]+a[3]+···+a[i] s[i]=a[1]+a[2]+a[3]++a[i]
区间 [ l , r ] [l,r] [l,r]内所有元素的和 s u m = s [ r ] − s [ l − 1 ] sum=s[r]-s[l-1] sum=s[r]s[l1]
可以只用一个数组,边输入,边得到前缀和数组,模板如下:

for(int i = 1; i <= n; ++i)
{
    scanf("%d", &arr[i]);
    arr[i]+=arr[i-1];
}

模板题:AcWing 795.前缀和
一道比较难的有关前缀和的题:AcWing 105.七夕祭

二维前缀和

穷学生没 i p a d ipad ipad,借鉴一下大佬画的图
在这里插入图片描述二维前缀和预处理公式: s [ i , j ] = s [ i , j − 1 ] + s [ i − 1 , j ] − s [ i − 1 , j − 1 ] + a r r [ i , j ] s[i,j]=s[i,j-1]+s[i-1,j]-s[i-1,j-1]+arr[i,j] s[i,j]=s[i,j1]+s[i1,j]s[i1,j1]+arr[i,j],询问 ( 1 , 1 ) (1,1) (1,1) ( i , j ) (i,j) (i,j)这个矩形内的所有元素之和直接就是 s [ i , j ] s[i,j] s[i,j]

查询某个小矩形内的和(再借下大佬的图)
在这里插入图片描述

矩形 ( x 1 , y 1 ) (x1,y1) (x1,y1) ( x 2 , y 2 ) (x2,y2) (x2,y2)内所有元素的和为
s u m = s [ x 2 , y 2 ] − s [ x 2 , y 1 − 1 ] − s [ x 1 − 1 , y 2 ] + s [ x 1 − 1 , y 1 − 1 ] sum=s[x_2,y_2]-s[x_2,y_1-1]-s[x_1-1,y_2]+s[x_1-1,y_1-1] sum=s[x2,y2]s[x2,y11]s[x11,y2]+s[x11,y11]
二维前缀和模板题链接:
AcWing 796.子矩阵的和 P S PS PS:这题还可以用压缩矩阵的方法来做)

差分

差分是前缀和的逆运算,有了差分数组,差分数组最方便的就是可以在 O ( 1 ) O(1) O(1)下进行区间修改,然后可以在 O ( n ) O(n) O(n)下查询到原数组经过修改后的数组

一维差分

原数组: a [ 1 ] a[1] a[1] a [ 2 ] a[2] a[2] a [ 3 ] a[3] a[3] a [ 4 ] a[4] a[4]···
差分数组: s [ 1 ] s[1] s[1] s [ 2 ] s[2] s[2] s [ 3 ] s[3] s[3] s [ 4 ] s[4] s[4]···
s [ 1 ] = a [ 1 ] − a [ 0 ] s[1]=a[1]-a[0] s[1]=a[1]a[0]
s [ 2 ] = a [ 2 ] − a [ 1 ] s[2]=a[2]-a[1] s[2]=a[2]a[1]
s [ 3 ] = a [ 3 ] − a [ 2 ] s[3]=a[3]-a[2] s[3]=a[3]a[2]
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ······
s [ i ] = a [ i ] − a [ i − 1 ] s[i]=a[i]-a[i-1] s[i]=a[i]a[i1]
原数组就是差分数组的前缀和数组
a [ 1 ] = s [ 1 ] a[1]=s[1] a[1]=s[1]
a [ 2 ] = s [ 1 ] + s [ 2 ] a[2]=s[1]+s[2] a[2]=s[1]+s[2]
a [ 3 ] = s [ 1 ] + s [ 2 ] + s [ 3 ] a[3]=s[1]+s[2]+s[3] a[3]=s[1]+s[2]+s[3]
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ······
a [ i ] = s [ 1 ] + s [ 2 ] + s [ 3 ] + ⋅ ⋅ ⋅ + s [ i ] a[i]=s[1]+s[2]+s[3]+···+s[i] a[i]=s[1]+s[2]+s[3]++s[i]

[ l , r ] [l,r] [l,r]区间内的每一个数都加上一个常数c,那么对差分数组的处理是 s [ l ] + = c , s [ r + 1 ] − = c s[l]+=c,s[r+1]-=c s[l]+=cs[r+1]=c,差分数组需要另开一个数组,而前缀和不用

模板题:AcWing 797.差分

二维差分

再用一下大佬画的好图
在这里插入图片描述

如何构造二维差分矩阵?

b b b a a a的差分矩阵,那么 a [ i ] [ j ] a[i][j] a[i][j]这个元素的值就是 b b b矩阵中 ( 1 , 1 ) (1,1) (1,1) ( i , j ) (i,j) (i,j)矩形内所有元素的和。

如果在 b b b矩阵中把 b [ n ] [ m ] b[n][m] b[n][m]这个元素加上常数 c c c,那么在 a a a中,对所有的 i ( i > = n ) , j ( j > = m ) i(i>=n),j(j>=m) i(i>=n),j(j>=m a [ i ] [ j ] a[i][j] a[i][j]都会加上 c c c
首先先说一下如何把 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2)的矩阵每个元素加一个常数 c c c,对 b b b矩阵有
b [ x 1 ] [ y 1 ] + = c , b[x_1][y_1]+=c, b[x1][y1]+=c,
b [ x 2 + 1 ] [ y 1 ] − = c b[x_2+1][y1]-=c b[x2+1][y1]=c
b [ x 1 ] [ y 2 + 1 ] − = c b[x_1][y_2+1]-=c b[x1][y2+1]=c
b [ x 2 + 1 ] [ y 2 + 1 ] + = c b[x_2+1][y_2+1]+=c b[x2+1][y2+1]+=c

首先把数组定义为全局变量,全局变量里面元素所有为0,零矩阵肯定是零矩阵的差分矩阵。那么我们在每输入一个原矩阵的值时就相当于在零矩阵中插入这个数,那么我们就可以同时构造出原矩阵的差分矩阵。那么由差分矩阵求原矩阵的时候就可以由二维前缀和来求
模板题如下:AcWing 798.差分矩阵
AC代码如下:

#include <iostream>
using namespace std;
const int MAXN=1010;
int a[MAXN][MAXN],b[MAXN][MAXN];
int n,m,q,x1,y1,x2,y2,c;

void insert(int x1,int y1,int x2,int y2,int c)
{
    b[x1][y1]+=c;
    b[x2+1][y1]-=c;
    b[x1][y2+1]-=c;
    b[x2+1][y2+1]+=c;
}

int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            scanf("%d",&a[i][j]);
            insert(i,j,i,j,a[i][j]);
        }
    while(q--)
    {
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
        insert(x1,y1,x2,y2,c);
    }
    
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+b[i][j];
            printf("%d ",b[i][j]);
        }
        printf("\n");
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值