链接:https://ac.nowcoder.com/acm/contest/888/A
来源:牛客网
All-one Matrices
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld题目描述
Gromah and LZR entered the great tomb, the first thing they see is a matrix of size n×mn\times mn×m, and the elements in the matrix are all 00_{}0 or 11_{}1.
LZR finds a note board saying "An all-one matrix is defined as the matrix whose elements are all 11_{}1, you should determine the number of all-one submatrices of the given matrix that are not completely included by any other all-one submatrices".
Meanwhile, Gromah also finds a password lock, obviously the password should be the number mentioned in the note board!
Please help them determine the password and enter the next level.
输入描述:
The first line contains two positive integers n,mn,m_{}n,m, denoting the size of given matrix.
Following nn_{}n lines each contains a string with length mm_{}m, whose elements are all 00_{}0 or 11_{}1, denoting the given matrix.
1≤n,m≤30001\le n,m \le 30001≤n,m≤3000
输出描述:
Print a non-negative integer, denoting the answer.示例1
输入
复制
3 4 0111 1110 0101输出
复制
5说明
The 5 matrices are (1,2)−(1,4), (1,2)−(2,3), (1,2)−(3,2), (2,1)−(2,3), (3,4)−(3,4)(1,2)-(1,4), \; (1,2)-(2,3), \; (1,2)-(3,2), \; (2,1)-(2,3), \; (3,4)-(3,4)_{}(1,2)−(1,4),(1,2)−(2,3),(1,2)−(3,2),(2,1)−(2,3),(3,4)−(3,4).
先对每个点的 高度(单调栈维护高度)、前缀和(判断当前行是否为矩阵的底)预处理
之后对每一行进行相同操作处理:
1.求当前点的矩阵的左端点
2.求当前点的矩阵的右端点(单调栈维护 最近的一个高度小于自己的下标,栈顶下标+/- 1为边界端点)
3.求当前行的矩阵对答案的贡献 并 去重处理(此处单调栈维护最左边的 高度<= 自己的下标,如果等于自己则为同一个矩阵不计算)
#include<bits/stdc++.h> using namespace std; char a[3004][3004]; int h[3004][3003],sum[3003][3003]; int R[3003],L[3003]; int m,n; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",a[i]+1); for(int j=1;j<=m;j++) { if(a[i][j]=='1') h[i][j]=h[i-1][j]+1;//高度预处理 sum[i][j]+=sum[i][j-1]+a[i][j]-'0';//前缀和预处理 } } int ans=0; for(int i=1;i<=n;i++) { stack<int> stk; stk.push(0); for(int j=1;j<=m;j++)//求L[j] 左边界 单调栈模板 { while(!stk.empty()&&h[i][j]<=h[i][stk.top()]) stk.pop(); //维护单调性处理 if(stk.empty()) L[j]=0;//h[i][j]高度为0时进入此语句 else L[j]=stk.top()+1;//当前栈顶已经是 最近的小于自己的数 stk.push(j); } while(!stk.empty()) stk.pop(); //清空栈 stk.push(m+1); for(int j=m;j;j--)//求R[j] 右边界 { while(!stk.empty()&&h[i][j]<=h[i][stk.top()]) stk.pop(); //维护单调性处理 if(stk.empty()) R[j]=0; else R[j]=stk.top()-1; stk.push(j); } while(!stk.empty()) stk.pop(); for(int j=1;j<=m;j++) { if(h[i][j]==0)//高度为0 不贡献答案 且清空单调栈 { while(!stk.empty()) stk.pop(); continue; } while(!stk.empty()&&h[i][j]<h[i][stk.top()]) stk.pop(); //维护单调性处理 if(stk.empty()||h[i][j]!=h[i][stk.top()])//去重判断--高度不相同 { int l=L[j]; int r=R[j]; if(sum[i+1][r]-sum[i+1][l-1]!=R[j]-L[j]+1)//下面对应的一行 无连续1 { ans++; } stk.push(j);//!!!!重新放入 } } } printf("%d\n",ans); }