算法模板,前缀和
前缀和
设存在数组 a1,a2,a3,a4,……an;
前缀和数组Si = ai + a2+ a3 +……+ai;这里一定要注意下标是从1开始;
前缀和的作用
能够快速求出原数组的一段和;
代码
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N], s[N];
int m,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;
}
代码分析
s[r] = a1 + a2 + a3 +a4 + a5+…… + al-1 + al + ……+ ar-1 + ar;
s[l-1] = a1+ a2 + a4 + a4 + a5 +……+ al-1;
上下两个式子进行相减就可以得到我们需要的式子了;
为什么下标从1开始:
在任何情况下保证s[r] -s[l-1] = al+……+ar;
二维前缀和
0、二维的前缀和表示ij这个点的左上方的矩阵的和
#include<iostream>
using namespace std;
const int N = 1010 ;
int n, m, q;
int a[N][N],s[N][N];
int main(void)
{
scanf("%d%d%d",&n,&m,&q);
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
scanf("%d",&a[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] + a[i][j];
while(q--)
{
int x1,x2,y1,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;
}
代码分析
s[i][j]表示ij点左上方矩形矩阵的和,计算的时候要注意减去重复的部分;
差分
0、前缀和 和差分是一对逆运算;
1.假设存在一个数组如 a1,a2,a3,a4,a5,……,an;构造一个数组b1,b2,b3,…bn;使得ai为bi的前缀和(A中的每一个元素是b的前缀和)则对b求一遍前缀和就可以得到a数组
2、差分的作用:由于a是b的前缀和,那么我们在B数组中的任意一项上添加一个常数c,那么对于A数组对应位置的那一项,对其以及其后面的每一个元素都加上了常数c ,这一点由前缀和的定义不难得到;
3、如何构造差分数组B:
在这里我们可一个据定义来求:
b1 = a1;
b2 = a2 -a1;
b3 = a3 -a2;
…
bn = an -an-1;
我们还有第二种方法:
插入法:我们根据0-2中所述的性质不难得出,如果我们想在一段A数组上进行常数的添加运算,我们可以利用在数组的最左边元素 l +=c,最右边元素的右边r+1位置进行-=c运算,如代码所示
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int n,m;
int a[N],b[N];
void insert(int l , int r, int c)
{
b[l] += c;
b[r+1] -= c;
}
int main(void)
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i ++ )
{
scanf("%d",&a[i]);
}
for(int i = 1; i <=n; i ++ )
insert(i,i,a[i]);
while(m--)
{
int l,r,c;
cin>>l>>r>>c;
insert(l,r,c);
}
for(int i = 1; i <= n ; i ++ )
{
b[i] += b[i-1];
printf("%d ",b[i]);
}
return 0;
}
二阶矩阵的差分
0、设存在A这个矩阵,则我们构造B矩阵,使得A中的ij是B中对应位置的前缀和;
1、二位矩阵的差分的构造方法:
在这里我们采用的还是利用那个插入的方法,先假设AB数组均初始化为0,然后依次插入元素;
#include<iostream>
using namespace std;
const int N = 1010;
int n,m,q;
int a[N][N],b[N][N];
void insert(int x1, int y1, int x2, int y2, int c)
{
b[x1][y1] += c;
b[x2+1][y1] -=c;
b[x1][y2+1] -= c;
b[x2+1][y2+1] +=c;
}
int main(void)
{
cin>>n>>m>>q;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
scanf("%d",&a[i][j]);
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
insert(i,j,i,j,a[i][j]);
while(q--)
{
int x1,x2,y1,y2,c;
cin>>x1>>y1>>x2>>y2>>c;
insert(x1,y1,x2,y2,c);
}
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++ )
b[i][j] += b[i-1][j ] + b[i][j-1] -b[i-1][j-1];
for(int i =1; i<=n;i++ )
{
for(int j = 1; j <= m; j ++ )
printf("%d ",b[i][j]);
puts("");
}
return 0;
}
代码分析
b[x1][y1] += c;
b[x2+1][y1] -=c;
b[x1][y2+1] -= c;
b[x2+1][y2+1] +=c;
在这里是那个插入函数,我们要在矩阵的一个子矩阵使得对用元素均加上一个常数;可以借助画图帮助理解;