洛谷 P1950 长方形

题目传送门

题目描述

小明今天突发奇想,想从一张用过的纸中剪出一个长方形。

为了简化问题,小明做出如下规定:

(1)这张纸的长宽分别为 n,m。小明讲这张纸看成是由n×m个格子组成,在剪的时候,只能沿着格子的边缘剪。

(2)这张纸有些地方小明以前在上面画过,剪出来的长方形不能含有以前画过的地方。

(3)剪出来的长方形的大小没有限制。

小明看着这张纸,想了好多种剪的方法,可是到底有几种呢?小明数不过来,你能帮帮他吗?

输入格式

第一行两个正整数 n,m,表示这张纸的长度和宽度。

接下来有 n 行,每行 m 个字符,每个字符为 * 或者 .。

字符 * 表示以前在这个格子上画过,字符 . 表示以前在这个格子上没画过。

输出格式

仅一个整数,表示方案数。

提供一个推导式;
当全部的方块都是未被填涂时;
以第i行,第j列的方块为长方形的右下角时的方案数:dp[i][j] = i * j;
然后就可以通过单调栈去做了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int max_n=1010;
bool g[max_n][max_n];
int Stack[max_n];
int maxa[max_n];
int dp[max_n];
int main(){
	int n,m;cin>>n>>m;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			char ch;cin>>ch;
			g[i][j] = (ch == '*');
		}
	}
	ll ans = (ll)n * (ll)(n + 1) * (ll)m * (ll)(m + 1) / 4;
	int top = 0;
	for(int i = 1; i <= n; i++){
		memset(dp, 0, sizeof(dp));
		memset(Stack, 0, sizeof(Stack));
		top = 0;
		for(int j = 1; j <= m; j++){
			if(g[i][j]) maxa[j] = i;
			while(top > 0 && maxa[Stack[top]] <= maxa[j]) top--;
			dp[j] = dp[Stack[top]] + maxa[j] * (j - Stack[top]);
			ans = ans -dp[j];
			Stack[++top] = j;
		}	
	}
	cout<<ans<<endl;
	return 0;
}
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页