为了能到远方,脚下的每一步都不能少.|

Shoukaku

园龄:7个月粉丝:2关注:3

ABC366D 题解

第一眼是想写 kdtree 的。

然后发现这就是一道三维前缀和的板子题。

三维前缀和

要想学习三维前缀和,我们首先得了解前缀和的概念,并且学会一维、二维前缀和。

什么是前缀和

前缀和是容斥原理的典型应用。这种优化方式可以使求和操作的时间复杂度降低到 O(1)(但是需要提前预处理)。

一维前缀和 & 二维前缀和

对于一维的一个序列 ai,如果我们有很多询问,每次询问让你求一个区间 [l,r] 的和,我们就可以使用一维前缀和来优化。

具体地,我们设前缀序列 gi=gi1+ai,那么不难发现,区间 [l,r] 的和就是 grgl1

对于一个二维的序列同理,我们设 gi,j=gi1,j+gi,j1gi1,j1+ai,j,就可以求出任意部分的和。

一维前缀和&二维前缀和

在一维序列中,红色部分的和就是黑色部分的和减掉绿色部分的和。那么前缀和的公式就是

在二维序列中,红色部分的和就是黑色部分的和减掉绿色部分的和,然后再加上黄色部分的和(因为黄色部分被减了 2 次)。

三维前缀和

三维前缀和同理,那么下面给出三维前缀和的图。

三维前缀和

可以发现...这完全看不懂啊!

没关系,我们将它拆开研究。

我们可以看到,黑色部分包括了要求的红色部分。那么我们就可以用黑色部分减掉剩余的部分,然后求出红色部分。

我们需要减掉这三个绿色部分:

减1

减2

减3

但是这时候又会发现一个问题,绿色部分的交(如图)被重复减了。

减交

所以我们需要找到绿色部分两两的交,并且加回来。即下图的黄色部分

加1

加2

加3

这时候,我们发现还是不对,黄色部分也有交!

加交

那么我们还需要将黄色部分的交(即下图粉色部分)再减掉。

减4

那么反过来,我们也可以推出前缀和的公式:

gi,j,k=gi1,j,k+gi,j1,k+gi,j,k1gi1,j1,kgi1,j,k1gi,j1,j1+gi1,j1,k1+ai,j,k


然后我们来看题。

可以发现这玩意就是一道三维前缀和的板子题,非常简单。

prefix[x][y][z] 为从 (1,1,1)(x,y,z) 的区域的前缀和,我们需要查询从 (Lx,Ly,Lz)(Rx,Ry,Rz) 的子立方体的和,公式为:

[Sum=prefix[Rx][Ry][Rz]]

[(Lx>1?prefix[Lx1][Ry][Rz]:0)]

[(Ly>1?prefix[Rx][Ly1][Rz]:0)]

[(Lz>1?prefix[Rx][Ry][Lz1]:0)]

[+(Lx>1 and Ly>1?prefix[Lx1][Ly1][Rz]:0)]

[+(Lx>1 and Lz>1?prefix[Lx1][Ry][Lz1]:0)]

[+(Ly>1 and Lz>1?prefix[Rx][Ly1][Lz1]:0)]

[(Lx>1 and Ly>1 and Lz>1?prefix[Lx1][Ly1][Lz1]:0)]


prefix[Rx][Ry][Rz] 是查询区域的总和的基础。

减去 (Lx1) 平面上的部分,表示区域中不包括 Lx 之前的部分。

减去 (Ly1) 平面上的部分,表示区域中不包括 Ly 之前的部分。

减去 (Lz1) 平面上的部分,表示区域中不包括 Lz 之前的部分。

加上 (Lx1,Ly1) 立方体的部分,因为这个区域被多次减去了。

加上 (Lx1,Lz1) 立方体的部分,因为这个区域被多次减去了。

加上 (Ly1,Lz1) 立方体的部分,因为这个区域被多次减去了。

减去 (Lx1,Ly1,Lz1) 立方体的部分,因为这个区域被多次加回来了。

code


#include "bits/stdc++.h"

using namespace std;

const int MAXN = 100; 

int A[MAXN+1][MAXN+1][MAXN+1];

int prefix[MAXN+1][MAXN+1][MAXN+1];

int main() {
	int n, q;
	
	
	cin >> n ;
	
	// 读取三维数组数据
	for (int x = 1; x <= n; ++x) {
		
		for (int y = 1; y <= n; ++y) {
			
			for (int z = 1; z <= n; ++z) {
				
				cin >> A[x][y][z];
				
			}
		}
	}
	
	// 计算三维前缀和
	for (int x = 1; x <= n; ++x) {
		
		for (int y = 1; y <= n; ++y) {
			
			for (int z = 1; z <= n; ++z) {
				
				prefix[x][y][z] = A[x][y][z]
				
				+ (x > 1 ? prefix[x-1][y][z] : 0)
				
				+ (y > 1 ? prefix[x][y-1][z] : 0)
				
				+ (z > 1 ? prefix[x][y][z-1] : 0)
				
				- (x > 1 && y > 1 ? prefix[x-1][y-1][z] : 0)
				
				- (x > 1 && z > 1 ? prefix[x-1][y][z-1] : 0)
				
				- (y > 1 && z > 1 ? prefix[x][y-1][z-1] : 0)
				
				+ (x > 1 && y > 1 && z > 1 ? prefix[x-1][y-1][z-1] : 0);
				
			}
			
		}
		
	}
	
	cin >> q;
	
	while (q--) 
	{
		
		int Lx, Rx, Ly, Ry, Lz, Rz;
		
		cin >> Lx >> Rx >> Ly >> Ry >> Lz >> Rz;
		
		
		// 前缀和查询范围内的和
		
		int result = prefix[Rx][Ry][Rz]
		
		- (Lx > 1 ? prefix[Lx-1][Ry][Rz] : 0)
		
		- (Ly > 1 ? prefix[Rx][Ly-1][Rz] : 0)
		
		- (Lz > 1 ? prefix[Rx][Ry][Lz-1] : 0)
		
		+ (Lx > 1 && Ly > 1 ? prefix[Lx-1][Ly-1][Rz] : 0)
		
		+ (Lx > 1 && Lz > 1 ? prefix[Lx-1][Ry][Lz-1] : 0)
		
		+ (Ly > 1 && Lz > 1 ? prefix[Rx][Ly-1][Lz-1] : 0)
		
		- (Lx > 1 && Ly > 1 && Lz > 1 ? prefix[Lx-1][Ly-1][Lz-1] : 0);
		
		cout << result << endl;
	}
	
	return 0;
}


本文作者:Shoukaku

本文链接:https://www.cnblogs.com/Xiang-he/p/18355037

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Shoukaku  阅读(17)  评论(0编辑  收藏  举报
//雪花飘落效果
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起