P3400 仓鼠窝 题解-单调栈典题
20231026
P3400 仓鼠窝 题解-单调栈典题
Statement
输出 01 矩阵中不含 0 的子矩阵的个数。\(n,m \le 3000\)
Solution
很妙的做法,典题,于是写了题解。
做法也很简单,就是你枚举每一个节点作为右上角的点的方案数,
发现其实有很多无用的点,比如有一个在你后面且比你高的点,
这样你就被淘汰了。
于是对于那些有用的点,我们可以直接用单调栈维护,
同时维护一个前缀和就是对答案的贡献。
具体实现就是枚举每一行,
再从左往右扫从而计算答案即可。
代码非常短。。。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e3+5;
int n,m,a[N][N],d[N],st[N],tp=0;
ll ans=0,s[N];
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int main(){
/*2023.10.26 H_W_Y P3400 仓鼠窝 单调栈*/
n=read();m=read();
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read();
for(int i=1;i<=n;i++,tp=0)
for(int j=1;j<=m;j++){
if(!a[i][j]) d[j]=i;
while(tp>0&&d[st[tp]]<d[j]) tp--;
st[++tp]=j;
ans+=(s[tp]=s[tp-1]+(i-d[st[tp]])*(st[tp]-st[tp-1]));
}
printf("%lld\n",ans);
return 0;
}
Conclusion
矩阵个数统计我们可以尝试维护每一个点为右上角的合法矩阵数量。