一、一维前缀和
数组: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;
}
};