-
前缀和
一维前缀和(One-dimensional prefix sum)
所谓一维,实际上就是一维数组,数组元素连续排列就可以看做一维的线,一维前缀和解决的主要问题是一维数组的区间和问题,即给定双指针l,r,求原数[l,r]内的和,利用数学上的数列来解决,即。()
构建一维前缀和:
由易知至少存在两个数值,所有我们把prefixsum[0]初始化为0,其后任意元素(第n个元素,n为正整数)减去prefixsum[0]得前n项和。
prefixsum数组的每一个元素都是每个n对应的= + 。
origin是从0索引开始的origin【i-1】对应第i个数
void Buildprefixsum1D(vector<int>& origin,vector<int>& prefixsum,int n) {
prefixsum[0]=0;
for(int i=1;i<=n;i++) {
prefixsum[i]=prefixsum[i-1]+origin[i-1];
}
}
求区间和:
中包含,所有如果只求得= - 。
求原数[l,r]内的和,利用数学上的数列来解决,即。()
void S(int l,int r,vector<int>& prefixsum) {
cout<<prefixsum[r]-prefixsum[l-1]<<endl;
}
总代码:
#include<iostream>
#include<vector>
using namespace std;
void Buildprefixsum1D(vector<int>& origin,vector<int>& prefixsum,int n) {
prefixsum[0]=0;
for(int i=1;i<=n;i++) {
prefixsum[i]=prefixsum[i-1]+origin[i-1];
}
}
void S(int l,int r,vector<int>& prefixsum) {
cout<<prefixsum[r]-prefixsum[l-1]<<endl;
}
int main() {
int n,m;
cin>>n;
vector<int> origin(n);
for(int i=0;i<n;i++) {
cin>>origin[i];
}//初始化原数组
vector<int> oneDprefixsum(n+1);
Buildprefixsum1D(origin,oneDprefixsum,n);
cin>>m;
for(int i=0;i<m;i++) {
int l,r;
cin>>l>>r;
S(l,r,oneDprefixsum);
}
return 0;
}
-
二维前缀和(Two-dimensional prefix sum)
二维前缀和的作用是求“面积”,首先我们有一个原始二位数组(origin[n][m]),这个二维数组可以看做二维平面,即由正方形组成的长为m,宽(高)为n的网格矩形,在此处引入一个新的二维数组prefixsum1D[n+1][m+1],这个数组内的元素代表以左上角(0,0)为一个左上端点,(i,j)为右下端点的矩形内原数组origin内所有在i,j矩形内元素的和。
如下图,原始数组的示意图,每个方格代表一个元素,而二位前缀和数组内的元素代表一片面积,令i=2,j=2,则的值就是图中所有元素的和,若i=1,j=2。则就是前2行所有数字的和。所有二位前缀和也被称作求面积。
首先给出公式:s代表二维前缀和,a代表原始数组
s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+a[i][j]
下图中,红色部分为(i,j),欲求前缀和S(i,j),绿色部分是S(i,j-1),蓝色部分是
S(i-1,j),紫色部分(i-1,j-1)我们求的S(i,j)实际上就是左上角4个正方形的元素的和,也就是一片面积,
s[i][j]=绿色+蓝色-紫色+红色
不难发现,计算 需要先获取3块面积(如果红色正方形也算入是4块面积,但红色数值已知),所有在二维前缀和数组prefixsum2D中要将第一行和第一列初始化为0。
构建二维前缀和数组:
这一次为了方便理解防止混淆,我们的原始数组origin是从1,1开始的,第一行和第一列都默认是0了(也就是说当做第一行第一列不存在了<实际上是0行0列不存在了>),这样原始数组和二维前缀和数组是重合的。
void Buildprefixsum2D(vector<vector<int>> &origin,int n,int m,vector<vector<int>> &prefixsum2D) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
prefixsum2D[i][j]=prefixsum2D[i-1][j]+prefixsum2D[i][j-1]-prefixsum2D[i-1][j-1]+origin[i][j];
}
}
}
欲求不以1,1为起点的面积,如图
欲求红色部分,有公式
红色=s[i][j]-s[i][b-1]-s[a-1][j]+s[a-1][b-1]
红色=S[i,j]-蓝色-绿色+紫色
如果只是求单一正方形的值(origin的元素只需令i=a,j=b即可)
求面积:
int S(vector<vector<int>> &prefixsum2D,int i,int j,int a,int b) {
return prefixsum2D[i][j]-prefixsum2D[i][b-1]-prefixsum2D[a-1][j]+prefixsum2D[a-1][b-1];
}
总代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void Buildprefixsum2D(vector<vector<int>> &origin,int n,int m,vector<vector<int>> &prefixsum2D) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
prefixsum2D[i][j]=prefixsum2D[i-1][j]+prefixsum2D[i][j-1]-prefixsum2D[i-1][j-1]+origin[i][j];
}
}
}
int S(vector<vector<int>> &prefixsum2D,int i,int j,int a,int b) {
return prefixsum2D[i][j]-prefixsum2D[i][b-1]-prefixsum2D[a-1][j]+prefixsum2D[a-1][b-1];
}
int main() {
int n,m,ans;
cin>>n;
m=n;
vector<vector<int>> origin(n+1,vector<int>(m+1));
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
cin>>origin[i][j];
}
}
vector<vector<int>> prefixsum2D(n+1,vector<int>(m+1,0));
Buildprefixsum2D(origin,n,m,prefixsum2D);
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
for(int a=1;a<i;a++) {
for(int b=1;b<j;b++) {
ans=max(S(prefixsum2D,i,j,a,b),ans);
}
}
}
}
cout<<ans<<endl;
return 0;
}
-
差分
-
一维差分(One-dimensional difference)
所谓差分,其实就是两个数的差,原数组每一个就是每个差分数组的。
实际上原数组是差分数组的前缀和,差分元素有正有负,使得差分的和被调整为原数组的元素。
差分的基本作用就是给一个区间的数进行加减处理,比如让【l,r】内都加上c,可以使原本
O(n)变为O(1),大大提高效率。
构建差分数组:
void Builddifference(vector<int> &prefixsum,int n,vector<int> &difference) {
for(int i=1;i<=n;i++) {
difference[i] = prefixsum[i] - prefixsum[i-1];
}
}
实现区间内的统一加减运算:
根据前缀和,如果在也就是 上加C,那从n=0开始累加,在n=l后的每一个原数组元素(前缀和)都增加了C,同理对减去C,那n=r+1后的每个原数组元素(前缀和)都减去C,抵消掉了原来的+C。
void function(vector<int> &difference,int l,int r,int c) {
difference[l] +=c;
difference[r+1] -=c;
}
总代码:
#include<iostream>
#include<vector>
using namespace std;
void Builddifference(vector<int> &prefixsum,int n,vector<int> &difference) {
for(int i=1;i<=n;i++) {
difference[i] = prefixsum[i] - prefixsum[i-1];
}
}
void function(vector<int> &difference,int l,int r,int c) {
difference[l] +=c;
difference[r+1] -=c;
}
int main() {
int n,p;
cin>>n>>p;
vector<int> prefixsum(n+1);
prefixsum[0] = 0;
for(int i=1;i<=n;i++) {
cin>>prefixsum[i];
}
vector<int> difference(n+1);
Builddifference(prefixsum,n,difference);
for(int i=1;i<=p;i++) {
int l,r,c;
cin>>l>>r>>c;
function(difference,l,r,c);
}
int ans=difference[1],temp=0;
for(int i=1;i<=n;i++) {
temp+=difference[i];
ans=min(ans,temp);
}
cout<<ans<<endl;
return 0;
}
-
二维差分(Two-dimensional difference)
二维差分实际上是使用了二维前缀和,对差分数组进行前缀和,以左上角为起点,(i,j)为右下角的终点,所得的面积就是原数组在(i,j)的元素值。
初始化:
初始化的过程其实就是进行加减C的过程,但实际是一类特殊情况,即只对一个元素进行操作,保证其他元素不受影响。(用c表a【i】【j】)
通过以上4次操作,使得只 b【i】【j】加上了C,也就是只有一块面积(一个正方形格子)加上了C。
根据公式易知操作数i,j存在,但i+1,j+1不一定存在,所以二维差分数字最外围一圈都是0。(方便对应并且防止溢出)
void Insert(vector<vector<int>> &initial,vector<vector<int>> &difference,int n,int m) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
difference[i][j]+=initial[i][j];
difference[i+1][j]-=initial[i][j];
difference[i][j+1]-=initial[i][j];
difference[i+1][j+1]+=initial[i][j];
}
}
}
矩形整体加减运算:
实际上是广泛化的初始化,原理相同。
void function(vector<vector<int>> &difference,int x1,int y1,int x2,int y2,int c) {
difference[x1][y1]+=c;
difference[x2+1][y1]-=c;
difference[x1][y2+1]-=c;
difference[x2+1][y2+1]+=c;
}
输出结果:
实际上就是进行二维前缀和
void Print(vector<vector<int>> &initial,vector<vector<int>> &difference,int n,int m) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
difference[i][j]=difference[i-1][j]+difference[i][j-1]-difference[i-1][j-1]+difference[i][j];
initial[i][j]=difference[i][j];
cout<<initial[i][j]<<" ";
}
cout<<endl;
}
}
总代码:
#include<iostream>
#include<vector>
using namespace std;
void Insert(vector<vector<int>> &initial,vector<vector<int>> &difference,int n,int m) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
difference[i][j]+=initial[i][j];
difference[i+1][j]-=initial[i][j];
difference[i][j+1]-=initial[i][j];
difference[i+1][j+1]+=initial[i][j];
}
}
}
void function(vector<vector<int>> &difference,int x1,int y1,int x2,int y2,int c) {
difference[x1][y1]+=c;
difference[x2+1][y1]-=c;
difference[x1][y2+1]-=c;
difference[x2+1][y2+1]+=c;
}
void Print(vector<vector<int>> &initial,vector<vector<int>> &difference,int n,int m) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
difference[i][j]=difference[i-1][j]+difference[i][j-1]-difference[i-1][j-1]+difference[i][j];
initial[i][j]=difference[i][j];
cout<<initial[i][j]<<" ";
}
cout<<endl;
}
}
int main() {
int n,m,p;
cin>>n>>p;
m=n;
vector<vector<int>> initial_array(n+2,vector<int>(m+2,0));
vector<vector<int>> difference(n+2,vector<int>(m+2,0));
/*for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
cin>>initial_array[i][j];
}
}
Insert(initial_array,difference,n,m);例题初始都为0无需初始化*/
for(int i=0;i<p;i++) {
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
function(difference,x1,y1,x2,y2,1);
}
Print(initial_array,difference,n,m);
return 0;
}
标签:origin,前缀,int,差分,C语言,vector,数组,difference
From: https://blog.csdn.net/2401_88542533/article/details/145281773