前缀和与分差【算法基础】

一、一维前缀和

数组:a1,a2,a3……an

前缀和:Si=a1+a2+……+ai

利用前缀和,可以求区间[l,r]的和。

步骤:

(1)先求出前缀和数组{S1,S2,S3……Sn}

(2)用公式求区间和S[r ]-S[l-r]

模板:

#include <iostream>
using namespace std;
const int N =100010;

int n,m;//n:整数个数,
int a[N],s[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
    while(m--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",s[r]-s[l-1]);
    }
    return 0;
}

二、二维前缀和

方法步骤:

(1)求出前缀和数组S[0][0]......S[i][j]

 s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1]

(2)根据公式求区间和

s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]

代码模板: 

#include<iostream>
using namespace std;
const int N=1010;
int n,m,q;//n行m列 问q组
int s[N][N];//会被初始化为0
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)//注意是从1开始
        for(int j=1;j<=m;j++)
            scanf("%d",&s[i][j]);
    //前缀和数组
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];

    while(q--)
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);

    }
    return 0;
}

三、差分

差分其实就是前缀和的逆运算

数组:a1,a2,a3……an

构造:b1,b2,b3,……bn

使得ai=b1+b2+……+bi

则称a是b的差分,b是a的前缀和。

模板:

#include<iostream>
using namespace std;
const int N=100010;
int n,m;
int a[N],b[N];
void insert(int l,int r,int c)//对a[l,r]的元素+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]);//把a看成前缀和数组
    
    for(int i=1;i<=n;i++)insert(i,i,a[i]);//a数组每个元素相当于对[i,i]区间的元素+a[i]
    //[1,1]+a[1]、[2,2]+a[2]……
    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];//b变回前缀和数组
    for(int i=1;i<=n;i++)printf("%d ",b[i]);
    
    return 0;
}

四、例题

和为k的子数组

 

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int n = nums.size();
        unordered_map<int,int> hash;
        hash[0] = 1;

        int sum = 0,ret= 0;
        for(auto x : nums)
        {
            sum += x;//计算当前位置前缀和
            if(hash.count(sum - k)) ret += hash[sum - k];
            hash[sum]++;
        }
        return ret;
    }
};

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值