2019年10月5日日常总结兼洛谷P1950题解

【题目】:
小明今天突发奇想,想从一张用过的纸中剪出一个长方形。
为了简化问题,小明做出如下规定:
(1)这张纸的长宽分别为 n , m n,m nm。小明讲这张纸看成是由 n × m n \times m n×m个格子组成,在剪的时候,只能沿着格子的边缘剪。
(2)这张纸有些地方小明以前在上面画过,剪出来的长方形不能含有以前画过的地方。
(3)剪出来的长方形的大小没有限制。
小明看着这张纸,想了好多种剪的方法,可是到底有几种呢?小明数不过来,你能帮帮他吗?
【思路】:
记未填充的点为好点,已填充的点为坏点。
u p [ i ] up[i] up[i]为第 i i i可以往上延伸多少
分别算出 l [ i ] l[i] l[i] r [ i ] r[i] r[i],分别表示 u p up up中左边第一个不大于 u p up up的数和右边第一个小于 u p up up的数(可以用单调栈写)
答案即为 ∑ i = 1 n u p [ i ] × ( i − l [ i ] ) × ( r [ i ] − i ) \sum ^n _{i=1} up[i] \times (i-l[i]) \times (r[i]-i) i=1nup[i]×(il[i])×(r[i]i)
【代码】:

//By HPXXZYY
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
const int N=1020;
ll l[N],r[N],up[N];
ll k[N],n,m,top,i,j;
int d[N][N];ll ans;
inline void calc_l(){
	top=0;int i;
	for(i=m;i;i--){
		while (top&&up[i]<=up[k[top]]){
			l[k[top]]=i;top--;
		}
		k[++top]=i;
	} 
	while (top){
		l[k[top]]=0;
		--top;
	}
}//利用单调栈求l数组
inline void calc_r(){
	top=0;int i=0;
	for(i=1;i<=m;i++){
		while (top&&up[i]<up[k[top]]){
			r[k[top]]=i;top--;
		}
		k[++top]=i;
	}
	while (top){
		r[k[top]]=m+1;
		--top; 
	}
}//利用单调栈求r数组
inline ll calc_answer(){
	calc_l();calc_r();
	register ll ans=0;
	for(int i=1;i<=m;i++)
	ans+=up[i]*(i-l[i])*(r[i]-i);
	return ans;
}//统计答案
int main(){
	cin>>n>>m;
	for(i=1;i<=n;i++)
	for(j=1;j<=m;j++){
		char ch;cin>>ch;
		if (ch=='*') d[i][j]=1;
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++){
			up[j]++;if (d[i][j]) up[j]=0;
		}
		ans+=calc_answer();
	} 
	cout<<ans;
	return 0;
}

本题题解主要来源于笔者上洛谷网校的课,思路来源于洛谷网校老师。如有侵权,请联系笔者,笔者立即删除!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值