前缀和是一种重要的预处理,能大大降低查询的时间复杂度。我们可以简单理解为“数列的前 n 项的和”。
下面是两道简单的前缀和题目
一、一维前缀和
题目链接:洛谷 P3406 海底高铁
就像公路上限速标志一样,只会在一个路段开始和结束两个地点声明限速和解除限速,而不必在全程每一根柱子上都标明限速70。那么这个题使用前缀和后也是一样,不必在每条路上都标记走了多少次,而只需要在开始和结束处做加一和减一标记处理。这样我们就知道接下来经过的这条路是被经过了多少次,而到了减一的城市之后,接下来经过的路段就与之前的路无关了。
代码:
#include <iostream>
#include <string.h>
using namespace std;
#define LL long long
int n, m, x, y;
LL ans = 0;
ll a, b, c, p[100005], val[100005];
LL Min(LL x, LL y) {
if (x < y)
return x;
else
return y;
}
LL Max(LL x, LL y) {
if (x > y)
return x;
else
return y;
}
int main() {
int n, m;
cin>>n>>m;
for(int i=1; i<=m; i++)
cin>>p[i];
memset(val, 0, sizeof(val));
for(int i=1; i<m; i++){
x = Max(p[i], p[i+1]);
y = Min(p[i], p[i+1]);
val[y]++;
val[x]--;
}
for(int i=1; i<=n; i++)
val[i] += val[i-1];
for(int i=1; i<n; i++) {
cin>>a>>b>>c;
ans += Min(a * val[i], b * val[i] + c);
}
cout<<ans<<endl;
return 0;
}
二、二维前缀和
题目链接:洛谷 P2004 领地选择
唯一需要注意的就是,设
s
[
i
]
[
j
]
s[i][j]
s[i][j]代表从(1, 1)到(i, j)这一子矩阵的元素和。则左上角坐标为
(
x
1
,
y
1
)
(x_1, y_1)
(x1,y1),右上角坐标为
(
x
2
,
y
2
)
(x_2, y_2)
(x2,y2)的子矩阵的元素之和为:
s
[
i
]
[
j
−
1
]
+
s
[
i
−
1
]
[
j
]
−
s
[
i
−
1
]
[
j
−
1
]
+
m
p
[
i
]
[
j
]
s[i][j-1] + s[i-1][j] - s[i-1][j-1] + mp[i][j]
s[i][j−1]+s[i−1][j]−s[i−1][j−1]+mp[i][j]。
代码:
#include <iostream>
#include <string.h>
using namespace std;
#define maxn 1005
int n, m, c, x, y;
int maxx = -0x7fffffff;
int mp[maxn][maxn], s[maxn][maxn];
int main() {
cin>>n>>m>>c;
//dp,求左上角坐标为(1,1),右下角坐标为(x,y)的子矩阵的元素和
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j) {
cin>>mp[i][j];
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + mp[i][j];
}
for(int i=c; i<=n; ++i)
for(int j=c; j<=m; ++j) {
if(s[i][j] - s[i-c][j] - s[i][j-c] + s[i-c][j-c] > maxx) {
maxx = s[i][j] + s[i-c][j-c] - s[i-c][j] - s[i][j-c];
x = i - c + 1;
y = j - c + 1;
}
}
printf("%d %d", x, y);
return 0;
}