CF 373D Counting Rectangles is Fun 单调栈+DP

题意:n*m的01矩阵,q次询问,每次询问左上角为(a,b),右下角为(c,d)的矩形中 有多少个全0的子矩形?
n,m<=40,q<=3e5.


n,m<=40 大胆的设状态 dp[a][b][c][d] 左上为(a,b)右下为(c,d)中有多少个全0子矩形.
类似二维前缀和 dp[a][b][c][d]=dp[a][b][c-1][d]+dp[a][b][c][d-1]-dp[a][b][c-1][d-1]+fun(a,b,c,d)
calc计算(c,d)为右下角,左上不超出(a,b)时,有多少个全0子矩形 总共O(n^6).


calc这部分暴力计算为O(n^2) 根据单调栈的经典应用:(c,d)为右下时,元素都相同的子矩形个数.
因为如果h[c][d]>h[c][d-1],则cnt[i][j]=cnt[i][j-1]+h[i][j]
根据右下(c,d)的宽度来分类计算,用单调栈来维护(c,d)前面第一个高度小于h[c][d]的位置k,中间部分个数为(d-k)*h[c][d]. 本题注意不超过左上边界即可.

时间复杂度O(n^4),

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=45;
int n,m,q,g[N][N],dp[N][N][N][N];
int h[N][N],cnt[N][N][N][N],s[N];
char t[N];
void solve()
{
	for(int a=1;a<=n;a++)
	{
		for(int b=1;b<=m;b++)
		{
			for(int c=a;c<=n;c++)
			{
				int top=0;
				for(int d=b;d<=m;d++)
				{
					if(g[c][d])
						top=0,cnt[a][b][c][d]=0,s[++top]=d;
					else
					{
						while(top&&h[c][d]<=h[c][s[top]])
							top--;
						int k=s[top];
						cnt[a][b][c][d]=cnt[a][b][c][k]+min((d-k),(d-b+1))*min(h[c][d],(c-a+1));//
				//		printf("%d %d %d %d %d\n",a,b,c,d,cnt[a][b][c][d]);
						s[++top]=d;	
					}
					dp[a][b][c][d]=dp[a][b][c-1][d]+dp[a][b][c][d-1]-dp[a][b][c-1][d-1]+cnt[a][b][c][d];	
				}
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",t+1);
		for(int j=1;j<=m;j++)
		{
			g[i][j]=t[j]-'0';
			if(g[i][j])
				h[i][j]=0;
			else
				h[i][j]=h[i-1][j]+1;
		}
	}
	solve();
	while(q--)
	{
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		printf("%d\n",dp[a][b][c][d]);
	}
	
	return 0;
}


这题O(n^5)也能通过,写起来简单点.还有一种是2维前缀和 然后又4维前缀和...什么鬼..

O(N^5) 

前缀和:

#include<stdio.h>
int map[50][50];
int rui[51][51];
int dat[50][50][50][50];
int ans[51][51][51][51];
int main()
{
	int mx,my,query;
	scanf("%d%d%d",&mx,&my,&query);
	for(int i=0;i<mx;i++)
	{
		for(int j=0;j<my;j++)
		{
			char zan;
			scanf(" %c",&zan);
			map[i][j]=zan-'0';
		}
	}
	for(int i=0;i<mx;i++)
	{
		for(int j=0;j<my;j++)
		{
			rui[i+1][j+1]=rui[i+1][j]+rui[i][j+1]-rui[i][j]+map[i][j];
		}
	}
	for(int i=0;i<mx;i++)
	{
		for(int j=0;j<my;j++)
		{
			for(int k=i;k<mx;k++)
			{
				for(int l=j;l<my;l++)
				{
					if(rui[k+1][l+1]-rui[i][l+1]-rui[k+1][j]+rui[i][j]==0)
					{
						dat[i][j][k][l]=1;
					}
				}
			}
		}
	}
	for(int i=0;i<mx;i++)
	{
		for(int j=0;j<my;j++)
		{
			for(int k=0;k<mx;k++)
			{
				for(int l=0;l<my;l++)
				{
					ans[i+1][j+1][k+1][l+1]
					=ans[i][j+1][k+1][l+1]
					+ans[i+1][j][k+1][l+1]
					+ans[i+1][j+1][k][l+1]
					+ans[i+1][j+1][k+1][l]
					-ans[i][j][k+1][l+1]
					-ans[i][j+1][k][l+1]
					-ans[i][j+1][k+1][l]
					-ans[i+1][j][k][l+1]
					-ans[i+1][j][k+1][l]
					-ans[i+1][j+1][k][l]
					+ans[i+1][j][k][l]
					+ans[i][j+1][k][l]
					+ans[i][j][k+1][l]
					+ans[i][j][k][l+1]
					-ans[i][j][k][l]
					+dat[i][j][k][l];
				}
			}
		}
	}
	for(int p=0;p<query;p++)
	{
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		a--;
		b--;
		c--;
		d--;
		printf("%d\n",
			ans[c+1][d+1][c+1][d+1]
			-ans[a][d+1][c+1][d+1]
			-ans[c+1][b][c+1][d+1]
			-ans[c+1][d+1][a][d+1]
			-ans[c+1][d+1][c+1][b]
			+ans[a][b][c+1][d+1]
			+ans[a][d+1][a][d+1]
			+ans[a][d+1][c+1][b]
			+ans[c+1][b][a][d+1]
			+ans[c+1][b][c+1][b]
			+ans[c+1][d+1][a][b]
			-ans[c+1][b][a][b]
			-ans[a][d+1][a][b]
			-ans[a][b][c+1][b]
			-ans[a][b][a][d+1]
			+ans[a][b][a][b]);
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值