B. 矩形个数
在一个由 0、1 元素构成矩阵中,统计至少含有 k个 1 的矩形的个数(矩形边界平行于矩阵边界)。
注意:单个元素也算是一个矩形。
输入格式
第一行,有四个空格分隔的整数
r
,
c
,
n
,
k
(
1
≤
r
,
c
,
n
≤
500
,
1
≤
k
≤
n
)
r,c,n,k ( 1≤r,c,n≤500,1≤k≤n )
r,c,n,k(1≤r,c,n≤500,1≤k≤n) 分别表示矩阵的行数,列数,矩阵中 1 的个数,和题意中给出的 k。
接下来 n 行,每行两个空格分隔的整数 x 和 y,表示每个 1 所在的位置 ( 1 ≤ x i ≤ r , 1 ≤ y i ≤ c ) ( 1≤x_i≤r,1≤y_i≤c) (1≤xi≤r,1≤yi≤c)
输出格式
输出1行1个数字,表示矩形的个数。
测试样例
Input
5 5 4 2
5 4
5 5
1 5
2 4
Output
41
思路:枚举上下边界,过程中压缩至一维,转化为求一维区间上区间和大于等于k的区间个数,枚举
O
(
n
2
)
O(n^2)
O(n2),区间个数用滑动窗口双指针
O
(
n
)
O(n)
O(n),故总时间复杂度
O
(
n
3
)
O(n^3)
O(n3)
考虑极端情况,所有的子矩阵都符合题意,故子矩阵个数为
C
501
2
∗
C
501
2
=
15687562500
>
I
N
T
_
M
A
X
C^2_{501}*C^2_{501} = 15687562500 > INT\_MAX
C5012∗C5012=15687562500>INT_MAX,所以答案要开long long int
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define ll long long int
const int N = 505;
int m, c, n, q;
int nums[N][N];
int ans[N];
int a[N];
long long int aa;
int main() {
cin >> m >> c >> n >> q;
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
nums[a][b] = 1;
}
for (int i = 1; i <= m; i++) {
memset(ans, 0, sizeof(ans));
for (int j = i; j <= m; j++) {
memset(a, 0, sizeof(a));
for (int k = 1; k <= c; k++)
ans[k] += nums[j][k];
int l = 1, r = 1;
for (int k = 1; k <= c; k++)
a[k] += a[k - 1] + ans[k];
while (l <= c && r <= c) {
while (l <= c && r <= c) {
if (a[r] - a[l - 1] >= q) {
aa += (c - r + 1);
l++;
r = l;
} else {
r++;
}
}
}
}
}
cout << aa;
return 0;
}
总结:求矩阵的子矩阵的题目,会很直观的想到枚举4个边界,再判断所围成的矩阵是否符合所求性质,时间复杂度可能会达到
O
(
n
5
)
O(n^5)
O(n5)甚至
O
(
n
6
)
O(n^6)
O(n6)。但许多类似的题都是通过固定上下边界后,再来对其中的性质进一步挖掘,比如悬线法和单调栈就是利用这一点成为求具有某种性质子矩阵的常用方法。
本题还有数据加强版(矩阵增大一维,但
k
<
=
10
k<=10
k<=10),
O
(
n
3
)
O(n^3)
O(n3)会被卡,我太菜了不会做= =