2020牛客多校第九场J-The Escape Plan of Groundhog
https://ac.nowcoder.com/acm/contest/5674/J
题意
给出一个只包含0、1的矩阵,求其中满足如下条件的子矩阵数
- 边界全部为1
- 内部1和0的数量差绝对值不超过1
题解
如果需要做到 \(O(n^3 )\),套路一般都是:枚举上下行边界,对于列扫一遍,用前缀和等维护。
那么这里四条边上都要为1,那么枚举上下边界后,肯定要找一段在这两行都是1的连续的列区间。然后在这个区间里找。
枚举每一列,如果这一列也都是1,就可以统计进去。用一个前缀和维护,在原矩阵中为0则当做-1,否则当做1。那么每次找到合法的列,查询前面的前缀和是否有和它相差1以内的,计入答案。然后把自己的前缀和加入统计。
\(O(n^2 m)\) 或$ O(nm^2 )$
队友一眼秒了,tql
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct READ {
inline char read() {
#ifdef _WIN32
return getchar();
#endif
static const int IN_LEN = 1 << 18 | 1;
static char buf[IN_LEN], *s, *t;
return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
}
template <typename _Tp> inline READ & operator >> (_Tp&x) {
static char c11, boo;
for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) {
if(c11 == -1) return *this;
boo |= c11 == '-';
}
for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0');
boo && (x = -x);
return *this;
}
} in;
const int N = 505;
int a[N][N];
int sum[N][N], check[N][N];
int t[N*N*3];
int s[N];
ll ans = 0;
void calc(int a, int b, int x, int y) {
s[x-1] = N * N;
vector<int> tmp;
for (int i = x; i <= y; i++) {
s[i] = s[i-1] + sum[b-1][i] - sum[a][i];
if (check[b][i] - check[a-1][i] == b - a + 1) {
ans += t[s[i-1]] + t[s[i-1]+1] + t[s[i-1]-1];
t[s[i]]++;
tmp.push_back(s[i]);
}
}
for (int e : tmp) t[e] = 0;
}
int main() {
int n, m; in >> n >> m;
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) in >> a[i][j];
for (int j = 1; j <= m; j++) {
for (int i = 1; i <= n; i++) {
sum[i][j] = sum[i-1][j] + (a[i][j] == 0 ? -1 : 1);//要加括号
check[i][j] = check[i-1][j] + a[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
int l = 1;
for (int k = 1; k <= m + 1; k++) {
if (a[i][k] == 0 || a[j][k] == 0) {
if (k - l >= 2) calc(i, j, l, k - 1);
l = k + 1;
}
}
}
}
printf("%lld\n", ans);
return 0;
}