[LUOGU]P3400 仓鼠窝

传送门

首先分析问题,我们要求出所有的子矩形,不妨考虑以每一点为右下角的子矩形的个数,加起来正好就是总的子矩形数了。

然后考虑每一个点为左下角时的方案数,我们考虑每在它左上的点是否可以作为矩形的左上角。

如图

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 }
View Code

猜你喜欢

转载自www.cnblogs.com/3soon/p/11530412.html