一维前缀和:
S[i] = a[1] + a[2] + ... + a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]
从S[1]开始,S[0]设为0
二维前缀和:
1、S[i][j]的推导:
代码:
for(i = 1 ; i <= n ; i ++)
{
for(j = 1 ; j <= m ; j ++)
{
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
2、子矩阵和
//S[i,j] = 第i行第j列格子左上部分所有元素的和
//求s[i][j]
for(i = 1 ; i <= n ; i ++)
{
for(j = 1 ; j <= m ; j ++)
{
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
//以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵的和为:
S[x2,y2] - S[x1 - 1,y2] - S[x2,y1 - 1] + S[x1 - 1,y1 - 1]
题目1:(一维前缀和)
题目描述:
输入一个长度为 n的整数序列。接下来再输入 m个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l个数到第 r个数的和。
输入格式
第一行包含两个整数 n和 m。
第二行包含 n个整数,表示整数数列。
接下来 m行,每行包含两个整数 l 和 r,表示一个询问的区间范围。
输出格式
共 m行,每行输出一个询问的结果。
数据范围
1 ≤ l ≤ r ≤ n,1 ≤ n,m ≤ 100000,
−1000 ≤ 数列中元素的值 ≤ 1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],s[N];
int main()
{
ios::sync_with_stdio(false); // 提高cin的读取速度
int i;
int n,m;
cin >> n >> m;
for(i = 1; i <= n; i ++) cin >> a[i];
for(i = 1 ; i <= n ; i ++) s[i] = s[i - 1] + a[i]; // 前缀和的初始化
while(m --)
{
int l,r;
int ans;
cin >> l >> r;
ans = s[r] - s[l - 1];
cout << ans << endl;
}
return 0;
}
题目2:(二维前缀和裸题)
题目描述:
输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式:
第一行包含三个整数 n,m,q。
接下来 n行,每行包含 m个整数,表示整数矩阵。
接下来 q行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
输出格式:
共 q行,每行输出一个询问的结果。
数据范围:
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int a[N][N],s[N][N];
int n,m,q;
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);
int i,j;
cin >> n >> m >> q;
for(i = 1 ; i <= n ; i ++)
{
for(j = 1 ; j <= m ; j ++)
{
cin >> a[i][j];
}
}
for(i = 1 ; i <= n ; i ++)
{
for(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,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] << endl;
}
return 0;
}
题目3:(二维前缀和)
题目描述
一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。
现在地图上有n(N ≤ 10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。
激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。
若目标位于爆破正方形的边上,该目标将不会被摧毁。
输入描述:
输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示 xi,yi ,vi 。
输出描述:
输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 5010; // N表示点最多的数量
int a[N][N];
int main()
{
int i,j,x,y,v;
int t,r;
int ans = 0;
cin >> t >> r;
int xx = r, yy = r; // xx和yy表示边界,初始化为最小的r
while(t --)
{
cin >> x >> y >> v;
x ++; y ++; // 坐标x,y都要加1,题目的坐标从0开始
a[x][y] = v;
xx = max(xx,x);
yy = max(yy,y);
}
for(i = 1 ; i <= xx ; i ++)
{
for(j = 1 ; j <= yy ; j ++)
{
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + a[i][j]; // 计算前缀和
}
}
for(i = r ; i <= xx ; i ++)
{
for(j = r; j <= yy ; j ++)
{
ans = max(ans,a[i][j] - a[i - r][j] - a[i][j - r] + a[i - r][j - r]); // 用提前算好的前缀和减去其他部分再补上多剪的那部分
}
}
cout << ans << endl;
return 0;
}