Combined Volume of Cuboids
An axis-aligned cuboid with parameters {(x_0, y_0, z_0), (delta x, delta y, delta z)} consists of all points (X, Y, Z) with x_0 <= X <= x_0 + delta x, y_0 <= Y <= y_0 + delta y, z_0 <= Z <= z_0 + d...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
An axis-aligned cuboid, specified by parameters $\{(x_0, y_0, z_0), (dx, dy, dz)\}$, consists of all points $(X,Y,Z)$ such that $x_0 \le X \le x_0 + dx$, $y_0 \le Y \le y_0 + dy$ and $z_0 \le Z \le z_0 + dz$. The volume of the cuboid is the product, $dx \times dy \times dz$. The combined volume of a collection of cuboids is the volume of their union and will be less than the sum of the individual volumes if any cuboids overlap.
Let $C_1, \dots, C_{50000}$ be a collection of $50000$ axis-aligned cuboids such that $C_n$ has parameters
\begin{align*} x_0 &= S_{6n - 5} \bmod 10000\\ y_0 &= S_{6n - 4} \bmod 10000\\ z_0 &= S_{6n - 3} \bmod 10000\\ dx &= 1 + (S_{6n - 2} \bmod 399)\\ dy &= 1 + (S_{6n - 1} \bmod 399)\\ dz &= 1 + (S_{6n} \bmod 399) \end{align*}
where $S_1,\dots,S_{300000}$ come from the "Lagged Fibonacci Generator":
For $1 \le k \le 55$, $S_k = [100003 - 200003k + 300007k^3] \pmod{1000000}$.
For $56 \le k$, $S_k = [S_{k -24} + S_{k - 55}] \pmod{1000000}$.
Thus, $C_1$ has parameters $\{(7,53,183),(94,369,56)\}$,
$C_2$ has parameters $\{(2383,3563,5079),(42,212,344)\}$, and so on.
The combined volume of the first $100$ cuboids, $C_1, \dots, C_{100}$, is $723581599$.
What is the combined volume of all $50000$ cuboids, $C_1, \dots, C_{50000}$?
Problem 212: Combined Volume of Cuboids
Mathematical Development
Definition 1. Let be a collection of axis-aligned cuboids in . The combined volume is , where denotes 3-dimensional Lebesgue measure.
Theorem 1 (Cavalieri’s principle for cuboid unions). Let be the distinct -coordinates among all faces . Then
where is the 2-dimensional Lebesgue measure of the union of the -projections of all cuboids active in the slab , i.e., those with and .
Proof. By Cavalieri’s principle, . For , the set of cuboids containing the plane is precisely the set of cuboids active in the slab , since no cuboid face lies strictly between and . Hence the cross-sectional area is constant on each open interval , equal to . Integrating over each slab gives , and summing over all slabs yields the total volume.
Theorem 2 (2D area via -sweep). For a fixed -slab, let be the set of active -rectangles. Let be the distinct -coordinates among all left and right edges of rectangles in . Then
where is the total length of the union of -intervals from rectangles in that cover the strip .
Proof. Apply Cavalieri’s principle in dimension 2: . Between consecutive -boundaries, the set of active rectangles is constant, so the -union length is constant on each strip.
Lemma 1 (Interval union via greedy merge). Given intervals sorted by left endpoint (), the union length is computed by the following algorithm in time:
- Initialize and .
- For : if , set and ; otherwise set .
- Set .
Then .
Proof. The algorithm maintains the invariant that is the current maximal merged interval. Since the intervals are sorted by left endpoint, any new interval either extends the current interval (if ) or starts a disjoint component (if ). In the latter case, is closed and its length added to . The final addition accounts for the last open interval. Correctness follows by induction on .
Editorial
We use dynamic programming over the state space implied by the derivation, apply each admissible transition, and read the answer from the final table entry.
Pseudocode
Z = sorted distinct set of {z0, z0 + dz} for all cuboids
V = 0
For j from 1 to |Z| - 1:
dz = Z[j+1] - Z[j]
active = {c in cuboids : c.z0 <= Z[j] and Z[j+1] <= c.z0 + c.dz}
If active is empty then continue
X = sorted distinct set of {c.x0, c.x0 + c.dx} for c in active
area = 0
For l from 1 to |X| - 1:
dx = X[l+1] - X[l]
intervals = [(c.y0, c.y0 + c.dy) : c in active, c.x0 <= X[l], X[l+1] <= c.x0 + c.dx]
If intervals is empty then continue
sort intervals by left endpoint
Ly = GreedyMergeLength(intervals)
area += dx * Ly
V += dz * area
Return V
Complexity Analysis
Let denote the number of cuboids.
Time. There are distinct -values, yielding slabs. For each slab, identifying the active set costs . The -sweep processes strips, and for each strip the -interval merge costs where is the number of active rectangles in that strip. Worst-case total: . In practice, the pseudorandom distribution over a grid produces sparse overlaps, yielding average-case behavior.
Space. for cuboid storage and working arrays.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
int main() {
const int NCUBOIDS = 50000;
const int SLEN = 6 * NCUBOIDS;
vector<long long> S(SLEN + 1);
for (int k = 1; k <= 55; k++) {
long long k3 = (long long)k * k * k;
S[k] = ((100003LL - 200003LL * k + 300007LL * k3) % 1000000LL + 1000000LL) % 1000000LL;
}
for (int k = 56; k <= SLEN; k++)
S[k] = (S[k - 24] + S[k - 55]) % 1000000LL;
struct Cuboid { int x1, y1, z1, x2, y2, z2; };
vector<Cuboid> cuboids(NCUBOIDS);
for (int n = 1; n <= NCUBOIDS; n++) {
int i = n - 1;
cuboids[i] = {
(int)(S[6*n-5] % 10000), (int)(S[6*n-4] % 10000), (int)(S[6*n-3] % 10000),
(int)(S[6*n-5] % 10000 + 1 + S[6*n-2] % 399),
(int)(S[6*n-4] % 10000 + 1 + S[6*n-1] % 399),
(int)(S[6*n-3] % 10000 + 1 + S[6*n] % 399)
};
}
vector<int> zcoords;
for (int i = 0; i < NCUBOIDS; i++) {
zcoords.push_back(cuboids[i].z1);
zcoords.push_back(cuboids[i].z2);
}
sort(zcoords.begin(), zcoords.end());
zcoords.erase(unique(zcoords.begin(), zcoords.end()), zcoords.end());
long long totalVolume = 0;
for (int zi = 0; zi + 1 < (int)zcoords.size(); zi++) {
int z0 = zcoords[zi], z1 = zcoords[zi + 1];
int dz = z1 - z0;
vector<int> active;
for (int i = 0; i < NCUBOIDS; i++)
if (cuboids[i].z1 <= z0 && z1 <= cuboids[i].z2)
active.push_back(i);
if (active.empty()) continue;
vector<int> localx;
for (int i : active) {
localx.push_back(cuboids[i].x1);
localx.push_back(cuboids[i].x2);
}
sort(localx.begin(), localx.end());
localx.erase(unique(localx.begin(), localx.end()), localx.end());
for (int xi = 0; xi + 1 < (int)localx.size(); xi++) {
int x0 = localx[xi], x1 = localx[xi + 1];
int dx = x1 - x0;
vector<pair<int, int>> yintervals;
for (int i : active)
if (cuboids[i].x1 <= x0 && x1 <= cuboids[i].x2)
yintervals.push_back({cuboids[i].y1, cuboids[i].y2});
if (yintervals.empty()) continue;
sort(yintervals.begin(), yintervals.end());
long long ylen = 0;
int cy0 = yintervals[0].first, cy1 = yintervals[0].second;
for (int j = 1; j < (int)yintervals.size(); j++) {
if (yintervals[j].first >= cy1) {
ylen += cy1 - cy0;
cy0 = yintervals[j].first;
cy1 = yintervals[j].second;
} else {
cy1 = max(cy1, yintervals[j].second);
}
}
ylen += cy1 - cy0;
totalVolume += (long long)dz * dx * ylen;
}
}
cout << totalVolume << endl;
return 0;
}
def solve():
NCUBOIDS = 50000
SLEN = 6 * NCUBOIDS
S = [0] * (SLEN + 1)
for k in range(1, 56):
S[k] = (100003 - 200003 * k + 300007 * k * k * k) % 1000000
for k in range(56, SLEN + 1):
S[k] = (S[k - 24] + S[k - 55]) % 1000000
cuboids = []
for n in range(1, NCUBOIDS + 1):
x0 = S[6 * n - 5] % 10000
y0 = S[6 * n - 4] % 10000
z0 = S[6 * n - 3] % 10000
dx = 1 + S[6 * n - 2] % 399
dy = 1 + S[6 * n - 1] % 399
dz = 1 + S[6 * n] % 399
cuboids.append((x0, y0, z0, x0 + dx, y0 + dy, z0 + dz))
zset = set()
for c in cuboids:
zset.add(c[2])
zset.add(c[5])
zcoords = sorted(zset)
total_volume = 0
for zi in range(len(zcoords) - 1):
z0, z1 = zcoords[zi], zcoords[zi + 1]
dz = z1 - z0
active = [c for c in cuboids if c[2] <= z0 and z1 <= c[5]]
if not active:
continue
xset = set()
for c in active:
xset.add(c[0])
xset.add(c[3])
xcoords = sorted(xset)
for xi in range(len(xcoords) - 1):
x0, x1 = xcoords[xi], xcoords[xi + 1]
dx = x1 - x0
yintervals = []
for c in active:
if c[0] <= x0 and x1 <= c[3]:
yintervals.append((c[1], c[4]))
if not yintervals:
continue
yintervals.sort()
ylen = 0
cy0, cy1 = yintervals[0]
for j in range(1, len(yintervals)):
if yintervals[j][0] >= cy1:
ylen += cy1 - cy0
cy0, cy1 = yintervals[j]
else:
cy1 = max(cy1, yintervals[j][1])
ylen += cy1 - cy0
total_volume += dz * dx * ylen
print(total_volume)
if __name__ == "__main__":
solve()