前缀和
小tips:如果变量定义成全局数组,初始为0;如果是局部数组,初始为随机值
前缀和:处理一个静态数组某一个区间内所有数的和的问题,有效提高算法效率
重新预处理一个数组S
S[0]=0
S[i]=a[1]+a[2]+……+a[i]
S[n]=a[1]+a[2]+……+a[n]
for(int i=1;i<=n;i++) S[i]=S[i-1]+a[i];
给定一段区间[L,R],求这段区间内的值=S[R]-S[L-1]=a[L]+a[L+1]+……+a[R ]
前缀和下标从1开始,就无需考虑边界问题,如果不是,则需要考虑
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];//表示原数组
int s[N];//表示前缀和数组
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) {
cin>>a[i];
s[i]=s[i-1]+a[i];
}
while(m--) {
int l,r;cin>>l>>r;
cout<<s[r]-s[l-1]<<'\n';
}
return 0;
}
差分
差分是前缀和的逆运算
给定a[1],a[2],……,a[n],构造差分数组b[N],使得a[i]=b[1]+b[2]+……+b[i]
a数组是b数组的前缀和
差分的核心操作:将a[L~R]全部加上C,等价于:b[L]+=C,b[R+1]-=C(公式)
①a[1~L-1]无影响
②a[L~R]加上了C
③a[R+1~N]无影响
价值:O(n)->O(1),提高效率
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],b[N];
int n,m;
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) {
cin>>a[i];
b[i]=a[i]-a[i-1];
}
while(m--) {
int l,r,c;cin>>l>>r>>c;
b[l]+=c;
b[r+1]-=c;
}
for(int i=1;i<=n;i++) {
a[i]=a[i-1]+b[i];
cout<<a[i]<<" ";
}
return 0;
}
子矩阵之和
二维矩阵,其实求每个子矩阵的和为多少
做法:二维前缀和,在前缀和数组中每个数表示的是原数组左上角的所有数之和
容斥原理的思想:①如何计算前缀和矩阵
S[x][y]=S[x-1][y]+S[x][y-1]-S[x-1][y]+a[x][y]
②如何利用前缀和矩阵,计算某个子矩阵的和
S[x1y1][x2y2]=S[x2][y2]-S[x2][y1-1]-S[x1-1][y2]+S[x1-1][y1-1]
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N][N],s[N][N];
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,m,q;cin>>n>>m>>q;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
cin>>a[i][j];
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
}
while(q--) {
int x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;
cout<<s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]<<'\n';
}
return 0;
}