P2036 - [蓝桥杯2022初赛] 统计子矩阵 - New Online Judgehttp://oj.ecustacm.cn/problem.php?id=2036二维前缀和暴力能过80%的数据
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll a[512][512] = {0};
ll sum[512][512] = {0}; // sum[i][j]表示从a[1][1]到a[i][j]的和
int main() {
ios::sync_with_stdio(false);
int n, m, k;
cin >> n >> m >> k;
ll ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) { // 左上角
for (int p = i; p <= n; p++) {
for (int q = j; q <= m; q++) { // 右下角
ll t = sum[p][q] - sum[p][j - 1] - sum[i - 1][q] + sum[i - 1][j - 1];
if (t <= k) {
ans++;
} else {
break;
}
}
}
}
}
cout << ans;
return 0;
}
AC代码:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 512;
int s[N][N] = {0}; // s[i]存第i行的前缀和
int main() {
ios::sync_with_stdio(false);
int n, m, k;
cin >> n >> m >> k;
ll ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> s[i][j];
s[i][j] += s[i][j - 1];
}
}
for (int j1 = 1; j1 <= m; j1++) {
for (int j2 = j1; j2 <= m; j2++) { // j1,j2枚举列数
int i2 = 0, sum = 0;
for (int i1 = 1; i1 <= n; i1++) {
while (sum <= k && i2 <= n) {
i2++;
sum += s[i2][j2] - s[i2][j1 - 1];
}
ans += i2 - i1;
sum -= s[i1][j2] - s[i1][j1 - 1]; // 减去第i1行的值
}
}
}
cout << ans;
return 0;
}
改成一维前缀和,j1、j2枚举左右两个列号,i1、i2枚举上下两个行号,在i1++,也就是往下移时,通过上一轮的sum减去第i1行的和可以直接得到第i1+1行到i2行的和,可以减少重复计算。
最后一重循环退出的条件为:sum>k或i2往下移到底了,此时以j1、j2为列号,i1为上行号的符合条件的个数为:i2-i1