XJTUOJ #1081 JM的赃物被盗

题目

https://oj.xjtuicpc.com/problem/1081

思路

蛮有意思的一道题,注意到\(q\leq 10^5\),暴力过不了,所以我们用前缀和。

然而这个前缀和是三维的,参照一维前缀和的原理,我们令\(pre[i][j][k]\)为以方块\((1,1,1)\)为左上角,以方块\((i,j,k)\)为右下角的长方体的所有方块的权值和。

那么怎样计算\(pre\)数组呢?分层处理,\(pre[i][j][k]=pre[i-1][j][k]+\sum_{x=1}^{j}\sum_{y=1}^{k} cost[i][x][y]\)

注意到后面是一个二维前缀和的形式,所以我们先令\(pre[i][j][k]=pre[i][j][k-1]+pre[i][j-1][k]+cost[i][j][k]-pre[i][j-1][k-1]\)(自行画图或脑补理解)。

这一层处理完后再令\(pre[i][j][k]=pre[i-1][j][k]\)

好了,pre数组搞完了。下面我们通过pre数组来求询问长方体的答案,不妨设这个答案为\(f(x1,y1,z1,x2,y2,z2)\)

好的,我们现在要求右下角的长方体(标红部分)的值,参照二维前缀和,我们猜想三维前缀和也是加加减减搞出来的,运用容斥原理,计算公式是这样的:

ll f(int x1,int y1,int z1,int x2,int y2,int z2){
	ll ans=pre[x2][y2][z2];
	ans-=pre[x2][y2][z1-1]+pre[x2][y1-1][z2]+pre[x1-1][y2][z2];
	ans+=pre[x2][y1-1][z1-1]+pre[x1-1][y2][z1-1]+pre[x1-1][y1-1][z2];
	ans-=pre[x1-1][y1-1][z1-1];
	return ans;
}

需要注意的是,尽管最终答案不超int,但中间结果可能会炸,保险起见开long long。

代码

#include<cstdlib>
#define ll long long
using namespace std;
ll cost[126][126][126],pre[126][126][126];
ll f(int x1,int y1,int z1,int x2,int y2,int z2){
	ll ans=pre[x2][y2][z2];
	ans-=pre[x2][y2][z1-1]+pre[x2][y1-1][z2]+pre[x1-1][y2][z2];
	ans+=pre[x2][y1-1][z1-1]+pre[x1-1][y2][z1-1]+pre[x1-1][y1-1][z2];
	ans-=pre[x1-1][y1-1][z1-1];
	return ans;
}
int main(){
	int i,j,k,q,a,b,c;
	int x1,x2,y1,y2,z1,z2;
	scanf("%d%d%d",&a,&b,&c);
	for(i=1;i<=a;i++)
		for(j=1;j<=b;j++)
			for(k=1;k<=c;k++)
				scanf("%lld",&cost[i][j][k]);
	for(i=1;i<=a;i++){
		for(j=1;j<=b;j++){
			for(k=1;k<=c;k++){
				pre[i][j][k]=pre[i][j-1][k]+pre[i][j][k-1]+cost[i][j][k]-pre[i][j-1][k-1];
			}
		}
		for(j=1;j<=b;j++){
			for(k=1;k<=c;k++){
				pre[i][j][k]+=pre[i-1][j][k];
			}
		}
	}
	scanf("%d",&q);
	for(i=1;i<=q;i++){
		scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
		printf("%lld\n",f(x1,y1,z1,x2,y2,z2));
	}
	// system("pause");
	return 0;
}
posted @ 2021-01-11 14:21  文艺平衡树  阅读(67)  评论(0编辑  收藏  举报