前缀和&差分

本文介绍了前缀和的概念及其在O(1)复杂度下的应用,包括一维和二维数组的前缀和计算模板。随后深入讲解了差分的定义,以及如何通过差分实现区间操作的快速计算。通过AcWing题库的两个实例,展示了如何构造和调用一维和二维差分。
摘要由CSDN通过智能技术生成

前缀和

解析

一、定义

​ 对于数组 A = { a 1 , a 2 , . . . , a n } A=\{a_1,a_2,...,a_n\} A={a1,a2,...,an},若存在数组 S S S 使得 S i = a 1 + . . . + a i S_i=a_1+...+a_i Si=a1+...+ai,那么就称数组 S S S 称为数组 A A A 的前缀和。形式基本表现为从原点开始的数据总和。

二、作用

①可以在 O ( 1 ) O(1) O(1) 的复杂度算出A数组中 a l a_l al a r a_r ar 之间所有数的和。

② 区间前缀和可以快速算出一个区间的值的和。

模板

//一维前缀和
for(int i=1;i<=n;i++){
	cin>>a[i];s[i]=s[i-1]+a[i];
}
a[l~r]=s[r]-s[l-1];
//二维前缀和
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1];
//三维前缀和
//构造
s[i][j][k]=a[i][j][k]+s[i-1][j][k]+s[i][j-1][k]+s[i][j][k-1]
			-s[i-1][j-1][k]-s[i-1][j][k-1]-s[i][j-1][k-1]
			+s[i-1][j-1][k-1];
//调用
a[x1y1z1~x1y1z2]=s[x2][y2][z2]
				-s[x2][y1][z2]-s[x1][y2][z2]-s[x2][y2][z1]
				+s[x1][y1][z2]+s[x1][y2][z1]+s[x2][y1][z1]
				-s[x1][y1][z1];

例题

  • 这里例题输入的数据量都比较大,建议使用scanf( );

一、795. 前缀和 - AcWing题库

#include <iostream>
using namespace std;
const int N=1e5+10;
int temp,s[N];
int n,k;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&temp);
        s[i]=s[i-1]+temp;
    }
    while(k--){
        int l,r;scanf("%d%d",&l,&r);
        printf("%d\n",s[r]-s[l-1]);
    }
    return 0;
}

二、796. 子矩阵的和 - AcWing题库

#include <iostream>
using namespace std;
const int N=1e3+10;
int s[N][N];
int n,m,q;
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int temp;scanf("%d",&temp);
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+temp;
        }
    }
    
    while(q--){
        int x1,x2,y1,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d\n",s[x2][y2]+s[x1-1][y1-1]-s[x1-1][ y2]-s[x2][y1-1]);
    }
    return 0;
}

差分

解析

​ 差分是前缀和的逆运算,若A数组的前缀和是B数组,那么A数组就是B数组的差分。

​ 对于数组 A = { a 1 , a 2 , . . . , a n } A=\{a_1,a_2,...,a_n\} A={a1,a2,...,an},若存在数组 B B B使得 a i = b 1 + . . . + b i a_i=b_1+...+b_i ai=b1+...+bi,那么就称数组B称为数组A的差分。

一维差分构造方法

b 1 = a 1 , b 2 = a 2 − a 1 , b 3 = a 3 − a 2 , . . . , b n = a n − a n − 1 b_1=a_1,b_2=a_2-a_1,b_3=a_3-a_2,...,b_n=a_n-a_{n-1} b1=a1,b2=a2a1,b3=a3a2,...,bn=anan1

验证: b 2 + b 1 = a 2 b_2+b_1=a_2 b2+b1=a2

可以使用 O ( n ) O(n) O(n)的时间从B数组得到A数组。

  • 作用(常用操作)

​ 在A数组的 [ l , r ] [l,r] [l,r]区间内加上c,即 a l + c , a l + 1 + c , . . . , a r + c a_l+c,a_{l+1}+c,...,a_r+c al+c,al+1+c,...,ar+c

​ 可以将上述操作转化为,在 b l + c b_l+c bl+c等于在 a l , a l + 1 , . . . , a n a_l,a_{l+1},...,a_n al,al+1,...,an上都加上c。但是我们不能改变 a r a_r ar之后的值,于是就将 b r + 1 − c b_{r+1}-c br+1c就行了。

​ 总结:在A数组的 [ l , r ] [l,r] [l,r]区间内加上c,等同于 b l + c b_l+c bl+c b r + 1 − c b_{r+1}-c br+1c

二维差分

​ 同样,二维差分叶可以看成是二维前缀和的逆运算。

​ 假设有一个二维矩阵 A i j A_{ij} Aij,其差分矩阵为 B i j B_{ij} Bij,使得 b i j b_{ij} bij到左上角矩形的和等于 a i j a_{ij} aij

  • 作用(常用操作)

​ 在二维矩阵 A i j A_{ij} Aij中的随机一个矩形,对其中每一个值加上c。

​ 与一维差分相同我们可以将上述操作进行一个转化,对 b i j + c b_{ij}+c bij+c即等于对 a i j a_{ij} aij一直到 a n n a_{nn} ann所围成的矩形都加上c,所以我们再将多余的部分减去就可以了。

​ 总结,在二维矩阵 A i j A_{ij} Aij中的随机一个矩形 ( x 1 , y 1 ) ; ( x 2 , y 2 ) (x_1,y_1);(x_2,y_2) (x1,y1);(x2,y2),对其中每一个值加上c,等同于 b x 1 y 1 + c b_{x_1y_1}+c bx1y1+c b x 2 + 1 , y 1 − c b_{x_2+1,y_1}-c bx2+1,y1c b x 1 y 2 + 1 − c b_{x_1y_2+1}-c bx1y2+1c b x 2 + 1 , y 2 + 1 + c b_{x_2+1,y_2+1}+c bx2+1,y2+1+c

模板

  • 一维差分
//构造一维差分
void insert(int l,int r,int c){
    b[l]+=c;
    b[r+1]-=c;
}
for(int i=1;i<=n;i++){
    scanf("%d",&a[i]);
    insert(i,i,a[i]);
}
//调用操作
while(m--){
    int l,r,c;
    scanf("%d%d%d",&l,&r,&c);
    insert(l,r,c);
}
//前缀和
for(int i=1;i<=n;i++)b[i]+=b[i-1];
  • 二维差分
//构造二维差分
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;
}
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--){
    int x1,x2,y1,y2,c;
    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];

例题

  • 一维差分

797. 差分 - AcWing题库

关键:在A数组的 [ l , r ] [l,r] [l,r]区间内加上c,等同于 b l + c b_l+c bl+c b r + 1 − c b_{r+1}-c br+1c

#include<iostream>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],b[N];
void insert(int l,int r,int c){
    b[l]+=c;
    b[r+1]-=c;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        insert(i,i,a[i]);
    }
    while(m--){
        int l,r,c;
        scanf("%d%d%d",&l,&r,&c);
        insert(l,r,c);
    }
    for(int i=1;i<=n;i++)b[i]+=b[i-1];
    
    for(int i=1;i<=n;i++)printf("%d ",b[i]);
    return 0;
}
  • 二维差分

798. 差分矩阵 - AcWing题库

#include<iostream>
using namespace std;
const int N=1e3+10;
int n,m,q;
int a[N][N],b[N][N];
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--){
        int x1,x2,y1,y2,c;
        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];
        
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)printf("%d ",b[i][j]);
        puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值