一类三维子长方体计数问题【计数】

Description

有一个 n ∗ m ∗ l n*m*l nml的长方体,每个位置有0/1的权值

对于每个为1的位置,要求包含这个位置且内部全为1的子长方体个数。
n ≤ 60 n\leq 60 n60

Solution

我们不妨考虑二维该如何数子矩形。

如果我们枚举每个合法的矩形,矩形内部+1,可以在左上角,右下角+1,左下右上-1,做一遍二维前缀和就可以得到答案,三维是类似的。

如果我们能求出每个位置分别作为左上角,右下角,左下角和右上角各出现了多少次,那么就可以直接统计了。

以左上角为例,预处理出每个位置向下距离第一个0还有多少个格子,从右向左扫每个格子,我们发现可以用来做右下角范围成一个递减的东西,可以利用单调栈来维护,我们只需要将贡献拆开,统计单调栈中每个位置的贡献和即可,这一部分可以做到 O ( n 2 ) O(n^2) O(n2)

拓展到三维,我们可以直接枚举第一维的上下边界,对后两维按照上述方法来做即可。
时间复杂度 O ( n 4 ) O(n^4) O(n4)

Code

#include 
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
const int N=62;
using namespace std;
intn,m,l,t,ls[N][N];
LL bz[N][N][N],ct[N][N][N];
bool mp[N][N];
int d[N];
int main()
{
     
	cin>>n>>m>>l;
	fo(i,1,n)
	{
     
		fo(j,1,m) fo(k,1,l)
		{
     
			int x;
			scanf("%d",&x);
			bz[i][j][k]=1-x;
		}
	}	
	fo(i,1,n) 
	{
     
		memset(mp,0,sizeof(mp));
		fo(j,i,n)
		{
     
			fo(u,1,m) fo(v,1,l) mp[u][v]|=bz[j][u][v];
			//左上,+ -
			fod(u,m,1) 
			{
     
				d[0]=1;
				d[1]=l+1;
				LL s=0;	
				fod(v,l,1) 
				{
     
					ls[u][v]=(mp[u][v])?0:ls[u+1][v]+1;
					while(d[0]&&ls[u][v]<ls[u][d[d[0]]])
					{
     
						s-=(LL)d[d[0]-1]*(ls[u][d[d[0]]]-ls[u][d[d[0]-1]]);
						d[d[0]--]=0;
					}
					d[++d[0]]=v;
					s+=(LL)(d[d[0]-1])*(ls[u][d[d[0]]]-ls[u][d[d[0]-1]]);
					ct[i][u][v]+=s-(LL)v*ls[u][d[d[0]]];
					ct[j+1][u][v]-=s-(LL)v*ls[u][d[d[0]]];
				}
			}
			//左下 - +
			fo(u,1,m) 
			{
     
				d[0]=1;
				d[1]=l+1;
				LL s=0;	
				fod(v,l,1) 
				{
     
					ls[u][v]=(mp[u][v])?0:ls[u-1][v]+1;
					while(d[0]&&ls[u][v]<ls[u][d[d[0]]])
					{
     
						s-=(LL)d[d[0]-1]*(ls[u][d[d[0]]]-ls[u][d[d[0]-1]]);
						d[d[0]--]=0;
					}
					d[++d[0]]=v;
					s+=(LL)(d[d[0]-1])*(ls[u][d[d[0]]]-ls[u][d[d[0]-1]]);
					ct[i][u+1][v]-=s-(LL)v*ls[u][d[d[0]]];
					ct[j+1][u+1][v]+=s-(LL)v*ls[u][d[d[0]]];
				}
			}
			//右上 - +
			fod(u,m,1) 
			{
     
				d[0]=1;
				d[1]=0;
				LL s=0;	
				fo(v,1,l) 
				{
     
					ls[u][v]=(mp[u][v])?0:ls[u+1][v]+1;
					while(d[0]&&ls[u][v]<ls[u][d[d[0]]])
					{
     
						s+=(LL)d[d[0]-1]*(ls[u][d[d[0]]]-ls[u][d[d[0]-1]]);
						d[d[0]--]=0;
					}
					d[++d[0]]=v;
					s-=(LL)(d[d[0]-1])*(ls[u][d[d[0]]]-ls[u][d[d[0]-1]]);
					ct[i][u][v+1]-=(LL)v*ls[u][d[d[0]]]+s;
					ct[j+1][u][v+1]+=(LL)v*ls[u][d[d[0]]]+s;
				}
			}
			//右下 + -
			fo(u,1,m) 
			{
     
				d[0]=1;
				d[1]=0;
				LL s=0;	
				fo(v,1,l) 
				{
     
					ls[u][v]=(mp[u][v])?0:ls[u-1][v]+1;
					while(d[0]&&ls[u][v]<ls[u][d[d[0]]])
					{
     
						s+=(LL)d[d[0]-1]*(ls[u][d[d[0]]]-ls[u][d[d[0]-1]]);
						d[d[0]--]=0;
					}
					d[++d[0]]=v;
					s-=(LL)(d[d[0]-1])*(ls[u][d[d[0]]]-ls[u][d[d[0]-1]]);
					ct[i][u+1][v+1]+=(LL)v*ls[u][d[d[0]]]+s;
					ct[j+1][u+1][v+1]-=(LL)v*ls[u][d[d[0]]]+s;
				}
			}
		}
	}
	
	fo(i,1,n) fo(j,1,m) fo(k,1,l)
	{
     
		ct[i][j][k]+=ct[i-1][j][k]+ct[i][j-1][k]+ct[i][j][k-1]-ct[i-1][j-1][k]-ct[i][j-1][k-1]-ct[i-1][j][k-1]+ct[i-1][j-1][k-1];
		printf("%d\n",ct[i][j][k]);
	}
}

你可能感兴趣的:(题解,好题,---计数,————单调栈)