思维 2020牛客暑期多校训练营(第九场) The Escape Plan of Groundhog
题目大意:
给一个01矩阵,问满足三个要求的子矩阵有多少个
- 边界全部都是1
- 内部的0和1的数量相差不能大于1
- 宽和高必须大于1
题解:
考虑把行或者列当作一个整体,以行当作整体为例,枚举两列,那么只要 (O(n)) 判断两列的答案即可。
两列的答案要按照这个性质来判断,可以考虑用前缀和维护,枚举一行,如果这一行全部都是1,那么就可以统计答案,如果不是那么就记录这个块的大小。
这个题目卡时间比较紧,所以不能用map,可以考虑用两个数组来模拟,一个表示已经到第几种状态了,每次map一清空,表示状态往后挪一位,然后一个数组来表示此时的大小。
#include <bits/stdc++.h>
#define debug(x) cout<<"debug:"<<#x<<" = "<<x<<endl;
using namespace std;
const int maxn = 6e5+10;
const int mod = 998244353;
typedef long long ll;
int a[550][550],sum[550][550],pre[550];
int mp[maxn],vis[maxn];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n; i++){
for(int j = 1;j <= m; j++){
scanf("%d",&a[i][j]);
if(!a[i][j]) a[i][j] = -1;
sum[i][j] = sum[i][j-1]+a[i][j];
}
}
int ans = 0,cnt = 1,M = 3e5;
for(int i = 1;i <= m; i++){
for(int j = i + 1;j <= m; j++){
int now = 0,cur = 0,first = 1;
cnt++;
for(int k = 1;k <= n; k++){
if(a[k][j]==1&&a[k][i]==1&&sum[k][j-1]-sum[k][i]==j-1-i){
++now;
pre[now] = pre[now-1] + cur;
if(first) pre[now] = 0,first = 0;
ans+=vis[pre[now]+M]==cnt?mp[pre[now]+M]:0;
ans+=vis[pre[now]+1+M]==cnt?mp[pre[now]+1+M]:0;
ans+=vis[pre[now]-1+M]==cnt?mp[pre[now]-1+M]:0;
pre[now] += sum[k][j-1]-sum[k][i];
if(vis[pre[now]+M]==cnt) mp[pre[now]+M]++;
else mp[pre[now]+M] = 1,vis[pre[now]+M] = cnt;
cur = 0;
}
else if(a[k][j]==1&&a[k][i]==1){
cur += sum[k][j-1] - sum[k][i];
}
else{
now = 0,cur= 0,first = 1;
cnt ++;
}
}
}
}
printf("%d
",ans);
return 0;
}