首先分析问题,我们要求出所有的子矩形,不妨考虑以每一点为右下角的子矩形的个数,加起来正好就是总的子矩形数了。
然后考虑每一个点为左下角时的方案数,我们考虑每在它左上的点是否可以作为矩形的左上角。
如图
01111
11011
10111
10111
1111X
我们考虑以X为子矩形右下角的的方案数,下图中#为可行的左上角
011##
110##
10###
10###
我们发现所有的'#'形成一个连通块,并且长度向上单调递减,递减的原因是'0'的限制,所以每一行最右边的'0'可以用一个数组维护,我们现在令第i行最右边的'0'的位置为height[i].
然后我们可以在扫描每一列时,用一个单调栈维护这个单调壳,并维护每一个位置的答案,每次望栈中添加元素时直接从上一状态转移答案,具体代码如下。似乎比别的题解短了一截。
P.S. 不用开读如优化,scanf就够用了(我猜cin也行
记得开long long
### copy from 这里
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define re register 4 #define LL long long 5 #define DB double 6 #define For(x,a,b) for(re int x=a;x<=b;x++) 7 #define For2(x,a,b) for(re int x=a;x>=b;x--) 8 #define LFor(x,a,b) for(re LL x=a;x<=b;x++) 9 #define LFor2(x,a,b) for(re LL x=a;x>=b;x--) 10 #define Abs(x) ((x>0)? x:-x) 11 int gi() 12 { 13 int res=0,fh=1;char ch=getchar(); 14 while((ch>'9'||ch<'0')&&ch!='-') ch=getchar(); 15 if(ch=='-') fh=-1,ch=getchar(); 16 while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar(); 17 return fh*res; 18 } 19 LL gl() 20 { 21 LL res=0,fh=1;char ch=getchar(); 22 while((ch>'9'||ch<'0')&&ch!='-') ch=getchar(); 23 if(ch=='-') fh=-1,ch=getchar(); 24 while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar(); 25 return fh*res; 26 } 27 LL n,m; 28 LL a[3005][3005]; 29 LL s[3005],f[3005],h[3005],t; 30 LL ans; 31 32 int main() 33 { 34 memset(f,0,sizeof(f)); 35 memset(s,0,sizeof(s)); 36 memset(h,0,sizeof(h)); 37 n=gl(),m=gl(); 38 LFor(i,1,n) 39 LFor(j,1,m) a[i][j]=gl(); 40 t=0; 41 ans=0; 42 LFor(i,1,n) 43 { 44 t=0; 45 LFor(j,1,m) 46 { 47 if(!a[i][j]) h[j]=i; 48 while(t&&h[s[t]]<h[j]) t--; 49 s[++t]=j; 50 f[t]=f[t-1]+(i-h[s[t]])*(s[t]-s[t-1]); 51 ans+=f[t]; 52 } 53 } 54 cout<<ans<<endl; 55 return 0; 56 }