/*
A. 这题用到了之前做过的一些题的一些思想:
例如:
1. 之前做过的 UVA - 1103 Ancient Messages ( http://blog.csdn.net/mofushaohua_ln/article/details/78162277 )
两题十分相似之处在于,都是连通块问题,且都要在最外围,加上一圈“空气”,以使得所有空气格子得以连通
2. 之前做过的 UVA - 221 Urban Elevations ( http://blog.csdn.net/mofushaohua_ln/article/details/77799245 )
两题都用到了坐标离散化的思想,来减少所需单元格的数目
不过需要注意的是,floodfill时,运用离散化后的坐标来处理连通块,是没有问题的。但是,在统计表面积和体积时,一定要用原始坐标,否则必定WA
B. 该题主要思路见小白书 P172
不考虑雕塑本身,而考虑“空气”,在网格周围加一圈“空气”(目的时为了让所有空气格子连通),然后做一次 floodfill,即可得到空气的 “内表面积” 和体积,这个表面积就是雕塑的外表面积,雕塑体积等于总体积减去空气体积
floodfill 时直接使用离散化后的新坐标,但在统计表面积和体积时则需要使用原始坐标
*/
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#define rep(i, n) for ( int i = 0; i < (n); i++ )
using namespace std;
const int N = 50 + 5;
const int C = 1e3 + 1;
int n, x0[N], y0[N], z0[N], x1[N], y1[N], z1[N];
// 离散化后的坐标
int nx, ny, nz;
int xs[N * 2], ys[N * 2], zs[N * 2];
// 种子填充
const int dx[] = { 1, -1, 0, 0, 0, 0 };
const int dy[] = { 0, 0, 1, -1, 0, 0 };
const int dz[] = { 0, 0, 0, 0, 1, -1 };
int color[N * 2][N * 2][N * 2];
struct Cell
{
int x, y, z;
Cell (int x = 0, int y = 0, int z = 0) : x(x), y(y), z(z)
{
}
bool valid() const
{
return x >= 0 && x < nx - 1 && y >= 0 && y < ny - 1 && z >= 0 && z < nz - 1;
}
bool solid() const
{
return color[x][y][z] == 1; // solid
}
bool getVis() const
{
return color[x][y][z] == 2; // visited
}
void setVis() const
{
color[x][y][z] = 2;
}
Cell neighbour (int dir) const
{
return Cell( x + dx[dir], y + dy[dir], z + dz[dir] );
}
int getVolume() const
{
return ( xs[x + 1] - xs[x] ) * ( ys[y + 1] - ys[y] ) * ( zs[z + 1] - zs[z] );
}
int getArea(int dir) const
{
if (dx[dir]) return ( ys[y + 1] - ys[y] ) * ( zs[z + 1] - zs[z] );
if (dy[dir]) return ( xs[x + 1] - xs[x] ) * ( zs[z + 1] - zs[z] );
return ( xs[x + 1] - xs[x] ) * ( ys[y + 1] - ys[y] );
}
};
void discretize (int* x, int& n) // 坐标离散化
{
sort(x, x + n);
n = unique(x, x + n) - x;
}
int ID (int *x, int n, int x0)
{
return lower_bound(x, x + n, x0) - x;
}
void FloodFill(int &v, int &s)
{
v = s = 0;
Cell c;
c.setVis();
queue<Cell> q;
q.push(c);
while (!q.empty())
{
Cell c = q.front();
q.pop();
v += c.getVolume();
rep(i, 6)
{
Cell c2 = c.neighbour(i);
if (!c2.valid()) continue;
if (c2.solid()) s += c.getArea(i);
else if (!c2.getVis())
{
c2.setVis();
q.push(c2);
}
}
}
v = C * C * C- v;
}
int main()
{
int t;
cin >> t;
while (t--)
{
nx = ny = nz = 2;
xs[0] = ys[0] = zs[0] = 0;
xs[1] = ys[1] = zs[1] = C;
cin >> n;
rep(i, n)
{
cin >> x0[i] >> y0[i] >> z0[i] >> x1[i] >> y1[i] >> z1[i];
x1[i] += x0[i]; y1[i] += y0[i]; z1[i] += z0[i];
xs[nx++] = x0[i]; xs[nx++] = x1[i];
ys[ny++] = y0[i]; ys[ny++] = y1[i];
zs[nz++] = z0[i]; zs[nz++] = z1[i];
}
discretize(xs, nx);
discretize(ys, ny);
discretize(zs, nz);
// paint
memset( color, 0, sizeof(color) );
rep(i, n)
{
int X1 = ID ( xs, nx, x0[i] ), X2 = ID ( xs, nx, x1[i] );
int Y1 = ID ( ys, ny, y0[i] ), Y2 = ID ( ys, ny, y1[i] );
int Z1 = ID ( zs, nz, z0[i] ), Z2 = ID ( zs, nz, z1[i] );
for (int X = X1; X < X2; X++)
for (int Y = Y1; Y < Y2; Y++)
for (int Z = Z1; Z < Z2; Z++)
{
color[X][Y][Z] = 1;
}
}
int v, s;
FloodFill(v, s);
cout << s << " " << v << endl;
}
}