单调栈求全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的子矩阵的左上角个数。
#include<cstdio>
typedef long long ll;
const int N=;
int a[N][N],sta[N],maxh[N];
ll res[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&a[i][j]);
int top;
ll all=(1ll*n*(n+)*m*(m+))>>,ans=0ll;//all总的方案数
for(int i=;i<=n;i++)
{
sta[top=]=;
for(int j=;j<=m;j++)
{
if(!a[i][j])//因为我们要求的是全1,所以维护这一列0的最大行数,也就是高度
maxh[j]=i;
while(top&&maxh[sta[top]]<=maxh[j])
top--;
res[j]=res[sta[top]]+1ll*(j-sta[top])*maxh[j];
ans+=res[j];
sta[++top]=j;
}
}
printf("%lld\n",all-ans);
return ;
}
单调栈哦
这题就是把数拆成31位,每一位有对应的权值。
#include<cstdio>
typedef long long ll;
const int N=,mod=;
int n,cf2[]={},a[N][N],h[N][N];
int sta[N],maxh[N];
ll res[N];
ll count(int k,int x)
{
for(int i=;i<=n;i++)
maxh[i]=;
int top=;
ll ans=;
for(int i=;i<=n;i++)
{
sta[top=]=;
for(int j=;j<=n;j++)
{
if(a[i][j]&cf2[k])
h[i][j]=x;
else
h[i][j]=x^;
if(!h[i][j])
maxh[j]=i;
while(top&&maxh[sta[top]]<=maxh[j])
top--;
res[j]=res[sta[top]]+1ll*(j-sta[top])*maxh[j];
if(res[j]>=mod)
res[j]-=mod;
ans+=res[j];
if(ans>=mod)
ans-=mod;
sta[++top]=j;
}
}
return ans;
}
int main()
{
for(int i=;i<=;i++)
cf2[i]=cf2[i-]<<;
scanf("%d",&n);
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
scanf("%d",&a[i][j]);
ll all=((1ll*n*(n+)*n*(n+))>>)%mod;
ll ans1=,ans2=;
for(int k=;k<=;k++)
{
ans1+=((all-count(k,)+mod)%mod)*1ll*cf2[k];
if(ans1>=mod)
ans1%=mod;
ans2+=count(k,)*1ll*cf2[k];
if(ans2>=mod)
ans2%=mod;
}
printf("%lld %lld\n",ans1,ans2);
return ;
}
单调栈哦哦
单调栈求全1(或全0)子矩阵的个数 洛谷P5300与或和 P3400仓鼠窝的更多相关文章
- 洛谷P3400 仓鼠窝(单调栈)
P3400 仓鼠窝 题目描述 萌萌哒的Created equal是一只小仓鼠,小仓鼠自然有仓鼠窝啦. 仓鼠窝是一个由n*m个格子组成的行数为n.列数为m的矩阵.小仓鼠现在想要知道,这个矩阵中有多少个子 ...
- 洛谷10月月赛Round.1| P3400 仓鼠窝[单调栈]
题目描述 萌萌哒的Created equal是一只小仓鼠,小仓鼠自然有仓鼠窝啦. 仓鼠窝是一个由n*m个格子组成的行数为n.列数为m的矩阵.小仓鼠现在想要知道,这个矩阵中有多少个子矩阵!(实际上就是有 ...
- 【DP/单调栈】关于单调栈的一些题目(codevs 1159,codevs 2673)
CODEVS 2673:Special Judge 题目描述 Description 这个月的pku月赛某陈没有参加,因为当时学校在考试[某陈经常逃课,但某陈还没有强大到考试也可以逃掉的程度].何 ...
- 【BZOJ5502】[GXOI/GZOI2019]与或和(单调栈)
[BZOJ5502][GXOI/GZOI2019]与或和(单调栈) 题面 BZOJ 洛谷 题解 看到位运算就直接拆位,于是问题变成了求有多少个全\(0\)子矩阵和有多少个全\(1\)子矩阵. 这两个操 ...
- HDU 6052 To my boyfriend(容斥+单调栈)
题意:对于一个n*m的方格,每个格子中都包含一种颜色,求出任意一个矩形包含不同颜色的期望. 思路: 啊啊啊啊啊,补了两天,总算A了这道题了,简直石乐志,前面的容斥还比较好写,后面的那个>13那个 ...
- 洛谷.5300.[GXOI/GZOI2019]与或和(单调栈)
LOJ BZOJ 洛谷 想了一个奇葩的单调栈,算的时候要在中间取\(\min\),感觉不靠谱不写了=-= 调了十分钟发现输出没取模=v= BZOJ好逗逼啊 题面连pdf都不挂了 哈哈哈哈 枚举每一位. ...
- SPOJ MINSUB - Largest Submatrix(二分+单调栈)
http://www.spoj.com/problems/MINSUB/en/ 题意:给出一个n*m的矩阵M,和一个面积k,要使得M的子矩阵M'的最小元素最大并且面积大于等于k,问子矩阵M'的最小元素 ...
- [GXOI/GZOI2019]与或和(位运算,单调栈)
题目链接懒得放了. 题目大意懒得写了. 省选原题哪有找不到的…… 说实话,其实这题是个大水题,被我十秒钟内口胡出来了. 首先位运算除了拆位还能干啥?以下以与为例,或是差不多的. 我们考虑有多少个子矩阵 ...
- 【洛谷5294】[HNOI2019] 序列(主席树维护单调栈+二分)
点此看题面 大致题意: 给你一个长度为\(n\)的序列\(A\),每次询问修改一个元素(只对当前询问有效),然后让你找到一个不下降序列\(B\),使得这两个序列相应位置之差的平方和最小,并输出这个最小 ...
随机推荐
- 剑指offer14:输入一个链表,输出该链表中倒数第k个结点。
1. 题目描述 输入一个链表,输出该链表中倒数第k个结点. 2. 思路和方法 可以用两个指针,一个指针遍历到第k个结点的时候,第二个指针再走到第一个节点,然后两个指针的距离始终保持k-1.这样,当第一 ...
- 第十二章 ZYNQ-MIZ701 PL中断请求
本篇文章主要介绍外设(PL)产生的中断请求,在PS端进行处理. 在PL端通过按键产生中断,PS接受到之后点亮相应的LED. 本文所使用的开发板是Miz701 PC 开发环境版本:Vivado 20 ...
- hdu 6069 Counting divisors 公式+区间筛
比赛的时候把公式扣出来了,,但是没有想到用筛法算公因子,,默默学习一下.. 题解:设n=p1^(c1)p2^{c2}...pm^{cm},n=p1^c1*p2^c2...p ...
- Your ApplicationContext is unlikely tostart due to a @ComponentScan of the defau
一.错误提示: Your ApplicationContext is unlikely tostart due to a @ComponentScan of the default package.. ...
- 又谈F分布
今天看到一篇不错的博文,有感,记录下来,相对来说讲到了本质,也很容易理解.https://www.cnblogs.com/think-and-do/p/6509239.html 首先,老生常谈,还是那 ...
- c# 粘贴复制
复制 1. 复制 Clipboard.SetText("123456"); Clipboard.SetImage(Image img); Clipboard.SetAudio(Sy ...
- Python中import导入上一级目录模块及循环import问题的解决
转自:https://www.cnblogs.com/sjy18039225956/p/9265461.html 使用python进行程序编写时,经常会使用第三方模块包.这种包我们可以通过python ...
- 原生js实现选项卡样式切换的几种方式。
先分享一个不能实现的实例(因为es5没有块作用域) for(var i=0; i<list.length; i++ ) { list[i].onclick = function(){ tabch ...
- 转 Git使用教程,最详细,最傻瓜,最浅显,真正手把手教
预警:因为详细,所以行文有些长,新手边看边操作效果出乎你的预料) 这个也不错 https://www.cnblogs.com/qcloud1001/p/9796750.html 一:Git是什么?Gi ...
- NSInvocation简单总结
(1)用法 NSInvocation是调用函数的另一种方式,它将调用者,函数名,参数封装到一个对象,然后通过一个invoke函数来执行被调用的函数,其思想就是命令者模式,将请求封装成对象. 例如,有这 ...