矩形个数问题(单调栈)

题目传送门

首先我们了解一下什么是单调栈(不过神犇们都应该会了)

1.定义

单调栈是一种可以以 O(n) 的时间复杂度解决类似求数组中某个位置i往左(或往右)第一个比i这个位置大(或小)的数(或位置)的问题的算法。
2.怎么做?

首先定义一个栈(先进后出)栈中存的是目前还没有答案的数,易得栈中元素递减(不然与栈的定义不符),对于每一次要将元素压入栈,先将栈顶所有大于它的元素记录答案(就是要压入的元素)并弹出,因为我们要求左边第一个,所以以从右向左的顺序入栈(因为是先入栈再记录答案,所以要从元素从答案入栈)

//代码,单调栈
void ddzl(){
    
    
    top=0;//清空栈
    for(int i=m;i>=1;i--){
    
    
        while(top!=0&&h[i]<=h[k[top]]) l[k[top]]=i,top--;//记录答案
        top++;
        k[top]=i;//入栈
    }
    while(top) l[k[top]]=0,top--;//对没有答案做特殊处理
}

为什么要用这个玩意(单调栈)?

先看个例子
n*m个格子中有多少个矩形?
在这里插入图片描述
设矩形的一竖边为x,横边为y;
对于同一行来说横边的长度可以为 1 , 2 , 3 …, m
而这些长度对应的不同位置条数为m ,m-1,m-2,…, 1
所以横边的数量为(1+m)*m/2
同理竖边的数量为(1+n)*n/2
所以共以共有n(1+m)m(1+n)/4条

那如果是这样的图案呢?
在这里插入图片描述
那就需要找一个完整的大矩形,再算矩形个数再相加,那这就需要用到单调栈

3.怎么算方案数?

定义 h[i] 为当前行第 i 列可向上最多能画多少
使用单调栈算出 l [ i ] 和 r [ i ],l [ i ] 是 h 中第i个位置左边第一个小于或等于h[i]的位置
r [ i ] 是 h 中第i个位置右边第一个小于h[i]的位置

为什么一个是小于等于一个是小于?

这类似于 [1,2)(前闭后开或前能取到,后取不到)(当然啦,你也可以前开后闭)
那样 [1,2),[2,3),[3,4)这样就不会重复计算。

举个例子
在这里插入图片描述
对每一列求出被这一列的高度限制的长方形数为
( i - l [ i ] )× ( r [ i ] - i ) × h [ i ]( i - l [ i ] )× ( r [ i ] - i )
表示某一行中的横边包括i这个位置的个数

在这里插入图片描述
比如上图i的左边能选3个,右边能选2个,那么覆盖i这个点的条数为2*3条

先上代码,先上代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005;
int n,m,s[N][N],l[N],r[N],h[N],k[N],top;
ll ans;
char p;
void ddzl()
{
    
    
	top=0;
	for(int i=m;i>=1;i--)
	{
    
    
		while(top!=0&&h[i]<=h[k[top]])l[k[top]]=i,top--;
		k[++top]=i;
	}
	while(top)l[k[top]]=0,top--;
}
void ddzr()
{
    
    
	top=0;
	for(int i=1;i<=m;i++)
	{
    
    
		while(top!=0&&h[i]<h[k[top]])r[k[top]]=i,top--;
		k[++top]=i;
	}
	while(top)r[k[top]]=m+1,top--;
}
void solve()
{
    
    
	ddzl();
	ddzr();
	for(int j=1;j<=m;j++)
	ans+=1ll*h[j]*(j-l[j])*(r[j]-j);
}
int main()
{
    
    
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
    
    
		for(int j=1;j<=m;j++)
		{
    
    
			cin>>p;
			if(p=='.')s[i][j]=0;
			else s[i][j]=1;
		}
	}
	for(int i=1;i<=n;i++)
	{
    
    
		for(int j=1;j<=m;j++)
		if(!s[i][j])h[j]++;
		else h[j]=0;
		solve();
	}
	cout<<ans<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_46001550/article/details/108596344