zoukankan      html  css  js  c++  java
  • 单调栈求全1(或全0)子矩阵的个数 洛谷P5300与或和 P3400仓鼠窝

      爆零好爽,被中学生虐好爽,还好我毕业得早

      求全1(或全0)子矩阵的个数,看了题解有好几种思路,我学了三种,但有两种不是很理解,而且也没另外那个跑得快,所以简单讲述一一下我会的那种来自Caro23333大佬的思路,传送门

      首先我们要知道,n*m矩阵的全部子矩阵的个数是C2n+1*C2m+1,因为n*m矩阵横的线就有n+1条,竖的线就有m+1条,而我们横的线任选两根,竖的线也任选两根就能组成一个矩阵了。我们求解的思路就是用总的矩阵数去减去不是全0(或者全1)的子矩阵数。那怎么去求出不是全0(或者全1)的子矩阵数?

      n*m的矩阵,以(n,m)格子作为右下角的子矩阵就有n*m个,因为它的左上角可以在n*m个格子里任意取。现在我们要求不是全0(或者全1)的子矩阵数,那么就是看那些格子作为它的左上角,使得矩阵包含0(或者1)。

      就比如上图,假设涂绿色的格子为0,其他全为1,现在我们求以红色格子为右下角,不是全1的子矩阵数。那么我们发现,以绿色区域内的格子作为左上角的子矩阵就不是全1的,因为他们的右下方存在着一个0。而绿色区域也很好求,假设一个绿色点是(x,y)那相应的绿色区域就是x*y。不过我们可以看出,绿色区域会有重复,这样的话会把一个区域算了多遍,那怎么处理这个重复的区域呢?

      单调栈!我们从上到下,从左到右遍历所有格子,维护maxh[i]为i这一列为0的格子的最大行数,那么我们单调栈维护i,j,i<j有maxa[i]<maxa[j]。严格单调递减的栈,这样的话就使得,当前格子是0的话覆盖的区域不会与前面的重复,因为

      当遍历到,(4,4)这个格子的时候,maxh[4]=4,然后栈顶保持的是2,maxh[2]=3,所以把2弹出栈,这时栈顶元素是0,对(4,4)这个右下角产生贡献的绿色区域就是,res[0]+(4-0)*maxh[4],从图中看就是第二个绿色区域。

      而当这个情况时,还是遍历到(4,4)这个格子的时候,maxh[4]=3,然后栈顶保持的是2,maxh[2]=4,所以2不用弹出栈,这时栈顶元素是2,对(4,4)这个右下角产生贡献的绿色区域就是res[2]+(4-2)*maxh[4]。从图中看就是,第一个绿色区域,以及第二个绿色区域不和第一个绿色区域重复的格子数。

      所以可以知道res是什么了,res[i]就是前i列最下面的那个0能够贡献的格子,也是覆盖的区域,那么res[i]=res[栈顶元素]+(j-栈顶元素)*maxh[i]//相当于宽乘高,当遍历第i行第j列时的res[j]就是(i,j)这个格子作为右下角时,不是全1的子矩阵的左上角个数。

    P3400仓鼠窝

     1 #include<cstdio>
     2 typedef long long ll;
     3 const int N=3233;
     4 int a[N][N],sta[N],maxh[N];
     5 ll res[N];
     6 int main()
     7 {
     8     int n,m;
     9     scanf("%d%d",&n,&m);
    10     for(int i=1;i<=n;i++)
    11         for(int j=1;j<=m;j++)
    12             scanf("%d",&a[i][j]);
    13     int top;
    14     ll all=(1ll*n*(n+1)*m*(m+1))>>2,ans=0ll;//all总的方案数 
    15     for(int i=1;i<=n;i++)
    16     {
    17         sta[top=0]=0;
    18         for(int j=1;j<=m;j++)
    19         {
    20             if(!a[i][j])//因为我们要求的是全1,所以维护这一列0的最大行数,也就是高度 
    21                 maxh[j]=i;
    22             while(top&&maxh[sta[top]]<=maxh[j])
    23                 top--;
    24             res[j]=res[sta[top]]+1ll*(j-sta[top])*maxh[j];
    25             ans+=res[j];
    26             sta[++top]=j;
    27         }
    28     }
    29     printf("%lld
    ",all-ans);
    30     return 0;
    31 }
    单调栈哦

    P5300 [GXOI/GZOI2019]与或和

      这题就是把数拆成31位,每一位有对应的权值。

     1 #include<cstdio>
     2 typedef long long ll;
     3 const int N=1108,mod=1000000007;
     4 int n,cf2[32]={1},a[N][N],h[N][N];
     5 int sta[N],maxh[N];
     6 ll res[N];
     7 ll count(int k,int x)
     8 {
     9     for(int i=1;i<=n;i++)
    10         maxh[i]=0;
    11     int top=0;
    12     ll ans=0;
    13     for(int i=1;i<=n;i++)
    14     {
    15         sta[top=0]=0;
    16         for(int j=1;j<=n;j++)
    17         {
    18             if(a[i][j]&cf2[k])
    19                 h[i][j]=x;
    20             else
    21                 h[i][j]=x^1;
    22             if(!h[i][j])
    23                 maxh[j]=i;
    24             while(top&&maxh[sta[top]]<=maxh[j])
    25                 top--;
    26             res[j]=res[sta[top]]+1ll*(j-sta[top])*maxh[j];
    27             if(res[j]>=mod)
    28                 res[j]-=mod;
    29             ans+=res[j];
    30             if(ans>=mod)
    31                 ans-=mod; 
    32             sta[++top]=j;
    33         }
    34     }
    35     return ans;
    36 }
    37 int main()
    38 {
    39     for(int i=1;i<=30;i++)
    40         cf2[i]=cf2[i-1]<<1;
    41     scanf("%d",&n);
    42     for(int i=1;i<=n;i++)
    43         for(int j=1;j<=n;j++)
    44             scanf("%d",&a[i][j]);
    45     ll all=((1ll*n*(n+1)*n*(n+1))>>2)%mod;
    46     ll ans1=0,ans2=0;
    47     for(int k=0;k<=30;k++)
    48     {
    49         ans1+=((all-count(k,1)+mod)%mod)*1ll*cf2[k];
    50         if(ans1>=mod)
    51             ans1%=mod;
    52         ans2+=count(k,0)*1ll*cf2[k];
    53         if(ans2>=mod)
    54             ans2%=mod;
    55     }
    56     printf("%lld %lld
    ",ans1,ans2);
    57     return 0;
    58 }
    单调栈哦哦
  • 相关阅读:
    【1】排行榜算法设计
    基础问答【二】
    基础问答【一】
    【1】c语言
    (五)帧同步与状态同步
    (四)c++虚函数详解
    (三)git pull报错解决方案,Your local changes to the following files would be overwritten by merge
    (二)干货!获取该目录下,指定权限不为770的文件, 并设置权限为770
    【8】java新特性,双冒号 :: 的使用场景
    go(01) 基础语法
  • 原文地址:https://www.cnblogs.com/LMCC1108/p/10876712.html
Copyright © 2011-2022 走看看