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

矩阵个数统计我们可以尝试维护每一个点为右上角的合法矩阵数量。

posted @ 2023-10-27 07:56  H_W_Y  阅读(24)  评论(0编辑  收藏  举报