题意: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]. 本题注意不超过左上边界即可.
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维前缀和...什么鬼..
前缀和:
#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]);
}
}