矩形统计【单调栈】

题目大意:

题目链接:http://10.156.31.134/contestnew.aspx?cid=128
求一个n×mn imes m的矩阵中选出小矩形的方案数,其中一部分格子不可以选。两个矩形不同定义为两个矩形大小不同或位置不同。


思路:

考虑使用容斥,答案即为总方案数减去选择了若干不可以选的格子的方案数。
总方案数非常简单,枚举矩形的右下端点,显然位于该点左上方的任意点均可与该点组成一个矩形。所以对于点(i,j)(i,j)一共有i×ji imes j中选法。
所以总方案数即为i=1nj=1mijsum^{n}_{i=1}sum^{m}_{j=1}ij
那么考虑选择若干坏点的方案数。依然枚举一个右下端点(i,j)(i,j),那么一个坏点可以对这个点产生贡献的充分条件即为该坏点位于端点的左上方。
同时,如果两个坏点(x1,y1)(x2,y2)(x1,y1)(x2,y2)均满足上述条件,但是有x1x2x1leq x2y1y2y1leq y2,那么显然(x1,y1)(x1,y1)(x2,y2)(x2,y2)完全包含住了,所以(x1,y1)(x1,y1)依然不可以对这个点产生贡献。
所以我们发现满足条件的坏点在横坐标单调递增的前提下一定满足纵坐标是单调递减的,否则必然存在产生不了贡献的点。
所以对于每一列维护一下能产生贡献的点集,利用求解即可。


代码:

#include <stack>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N=1010;
ll maxn,sum,last[N][N],f[N][N];
int n,a[N][N];
stack<ll> s[N];

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			scanf("%1d",&a[i][j]);
	for (int i=1;i<=n;i++)
		s[i].push(0);
	for (ll i=1;i<=n;i++)
		for (ll j=1;j<=n;j++)
		{
			if (a[i][j]) last[i][j]=j;
				else last[i][j]=last[i][j-1];
			while (s[j].size()>1 && last[s[j].top()][j]<last[i][j]) s[j].pop();
			maxn+=i*j;
			f[i][j]=f[s[j].top()][j]+(i-s[j].top())*last[i][j];
			sum+=f[i][j];
			s[j].push(i);
		}
	printf("%lld
",maxn-sum);
	return 0;
}
原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998074.html