题目
https://oj.xjtuicpc.com/problem/1081
思路
蛮有意思的一道题,注意到(qleq 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
",f(x1,y1,z1,x2,y2,z2));
}
// system("pause");
return 0;
}