1.前缀和、差分定义
1.1一维
1)前缀和:某项为 该项及该项前 的数据和
//数组a (下标为0开始)变前缀和数组
for(int i=1;i<n;i++)a[i]+=a[i-1];
2)差分:某项变为 该项与前一项的差
2).1用途:维护一个区间的快速修改
2).2性质:①差分序列求前缀和可得原序列
②将原序列区间【L,R】中的元素加q可以转化为L处+q,R+1处-q;
1.2二维
发现了一篇很好的东西(但是我感觉在二位差分那差一点点意思)
1)前缀和:
1).1定义:求一个矩阵内的从(0,0)到(i,j)的子矩阵的数的和
1).2用途:一般运用在动态规划
sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]
1).3推广:从(x1,y1)到(x2,y2)的矩阵和
sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][x1-1]
2)差分:
从(x1,y1)到(y1,y2)的子矩阵中每个值都 加a
eg:黄色区域中的值均加a,那么就是 红+绿+蓝+黄 中的值均加a,再 绿+红 中的值均减a,蓝+红 中的值均减a,最后 红中的值加一个a
df[x2+1][y2+1]+=a;df[x1][y1]+=a;
df[x1][y2+1]-=a;d[x2+1][y1]-=a;
1.3小小的总结
前缀和 主要用于对数据区间的查询
差分 主要用于对数据的区间维护(仅限加减)
(以后补一下 树上前缀和 与 树上差分)
2.习题
2.1一维
HJ浇花-前缀和+差分
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e6+10;
int n,l,r,m_r,df[M]cnt[200010];
int main(){
ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
cin>>n;
m_r=0;
for(int i=1;i<=n;i++){
cin>>l>>r;
df[l]++;df[r+1]--;//差分数组
m_r=max(m_r,r+1);
}
for(int i=1;i<=m_r;i++)df[i]+=df[i-1];//前缀和变原数组
for(int i=0;i<=m_r;i++)cnt[df[i]]++;
cout<<cnt[1];
for(int i=2;i<=n;i++)cout<<" "<<cnt[i];cout<<endl;
return 0;
}
2.2二维
洛谷 P1387 最大正方形-二维前缀和的应用
此题数据小所以可用此法,一般用dp(待补)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e6+10;
int n,m,prs[110][110],i,ans;
int main(){
ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>prs[i][j];
prs[i][j]+=prs[i-1][j]+prs[i][j-1]-prs[i-1][j-1];//前缀和
}
}
//
for(i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int l=1;l<=min(i,j);l++){//从当前格子开始正方形的判断及记录最大边长
if(l*l== prs[i][j]-prs[i-l][j]-prs[i][j-l]+prs[i-l][j-l] )//二维前缀和的推广
ans=max(l,ans);
else break;
}
}
}
cout<<ans;
return 0;
}
洛谷 P3397 地毯-二维差分
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e6+10;
int n,m,df[1010][1010];
int main(){
ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
df[x1][y1]++; df[x2+1][y2+1]++;
df[x1][y2+1]--; df[x2+1][y1]--;
}//区间维护-二维差分
//取前缀和 变成原序列
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
df[i][j]+=df[i-1][j]+df[i][j-1]-df[i-1][j-1];
if(j!=1)cout<<" "<<df[i][j];
else cout<<df[i][j];
}
cout<<endl;
}
return 0;
}