ACM-ICPC 2018 南京赛区网络预赛 B. The writing on the wall
题目
给一个 n * m 的矩阵,和 k 个矩阵上的污点,问没有在污点上的子矩阵有多少。
分析
首先想如果没有污点,怎么算所有矩阵。
枚举子矩阵右下角,n * m种情况。在确定子矩阵右下角为 (i, j)时,考虑长宽,因为本题 m 范围 100,所以枚举宽,1~m,每种宽对应 i 种高。代码:
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
for(int k = j; k >= 1; k--)
cnt += i;
如果有污点,还是枚举宽,但是高会有限制,用 limit 数组更新每一行的限制。
上面总体暴力做法 O(n * m * m)。数据水能过,,
正确做法单调栈维护最近的限制,我不会。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 10;
const int M = 1e2 + 10;
int a[N][M], cas = 1, _, n, m, k;
int limit[M];
int main(){
for (scanf("%d", &_); _; _--){
memset(a, 0, sizeof(a));
memset(limit, 0, sizeof(limit));
scanf("%d%d%d", &n, &m, &k);
for(int i = 1, x, y; i <= k; i++){
scanf("%d%d", &x, &y);
a[x][y] = 1;
}
ll ans = 0;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(a[i][j])
limit[j] = i;
}
for (int j = 1; j <= m; j++){ // 枚举右下角 (i, j);
int h = i;
for(int k = j; k >= 1; k--){ // 有多少种 宽, 往左数不断更新最低限制
h = min(h, i - limit[k]);
ans += h; // 每次加高
}
}
}
printf("Case #%d: %lld\n", cas++, ans);
}
return 0;
}