0%

2026 杭电暑期多校 6 ——1002-Dice Tower-骰子塔(小心,舞台最下面的那个面也会贡献)

题目大意

Dice Tower

时间限制: 2000 MS | 内存限制: 524288 KB

题目描述

联合演出结束后,Ave Mujica 的舞台机关还没有拆。睦留下了一批骰子道具,祥子想把它们堆成一座能从观众席各个方向看到的骰子塔;一旁的爱音则认真研究起怎样摆才能让露出的点数更多。

祥子在舞台平面上画出了一个 nnmm 列的网格。第 ii 行第 jj 列的位置上堆着若干个完全相同的单位骰子。从正上方看,第 ii 行第 jj 列的骰子塔高度为 hi,jh_{i,j},也就是说这个位置上堆了 hi,jh_{i,j} 个骰子。

所有骰子都与网格对齐,且同一个格子里的骰子上下紧贴摆放;若试图把它们摆歪,会被祥子立刻制止。

每个骰子的 66 个面分别有 1,2,3,4,5,61, 2, 3, 4, 5, 6 个点,且相对两面的点数和为 77。睦提醒大家,骰子各个面的相对位置固定:初始时,上、下、前、后、左、右六个面的点数依次为 1,6,2,5,3,41, 6, 2, 5, 3, 4;之后只能通过旋转改变朝向,不能将骰子翻成镜像。

爱音可以任意旋转每个骰子,并且不同骰子的朝向可以不同。相邻两个骰子贴在一起的面不会露出。一个骰子对答案的贡献等于它所有露出面的点数之和。

请你帮爱音求出所有骰子的贡献之和最大可以是多少。

输入格式

第一行包含一个整数 TT1T1051 \le T \le 10^5),表示测试数据的组数。

对于每组测试数据:

  • 第一行包含两个整数 n,mn,m1n,m1031 \le n,m \le 10^3),表示网格的行数和列数。

  • 接下来 nn 行,每行包含 mm 个整数,其中第 ii 行第 jj 个整数为 hi,jh_{i,j}0hi,j1090 \le h_{i,j} \le 10^9),表示该位置上骰子塔的高度。

保证对于所有的测试数据,满足 n×m106\sum n \times m \le 10^6

输出格式

对于每组测试数据,输出一行一个整数,表示该组测试数据中露出面的最大点数之和。

样例输入

Plaintext

1
2
3
4
5
6
7
8
2
2 2
1 2
3 4
3 4
0 2 0 1
3 1 4 0
0 2 2 5

样例输出

Plaintext

1
2
156
314

样例解释

样例中的两组测试数据分别对应原来的两座骰子塔。

在第一组数据中,骰子塔的普通外表面积为 3434,但本题计算的是露出面上的点数之和。通过合理旋转每个骰子,可以使露出面的点数之和达到 156156

注意: 最底层的骰子的下表面(即与舞台平面接触的面)也计入露出的表面并产生点数贡献

思路讲解

那么这种实现方式比较复杂,是因为它没有实现轴的单独判别。

它是用面数进行判别的,那么实际上我们可以把单个块的逻辑给抽离出来。我们没有必要在遍历的时候,在同一个地方书写计算的逻辑。

这个函数的计数的计数原理就是我们知道一个相对面组,它的和一定是7,所以说如果凑齐了一个相对面,那么它的答案一定是贡献7,我们称一个相对面组为一个轴,轴中如果只有一个面,那么就是贡献6,5,4,我们优先给他分配比较高的面。

这个就是 axis1,acc1,和 origin_cost 的原理。

1
2
ll acc1 = accumulate(all(axis1), 0ll);
ll res = ... + origin_cost[acc1];

完整的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
i128 origin_cost[] = {0, 6, 11, 15};
// X, Y是坐标,H是该点高度,ver是该点暴露在外的垂直面数量(夹在中间就是0,头尾就是1,2 只有块高度为一的时候)
i128 cal_single(ll x, ll y, ll h, ll ver) {
if (h == 0) return 0;
vector<ll> axis1(3), axis2(3);
if (ver == 1) {
axis1[2]++;
} else if (ver == 2) {
axis2[2]++;
}
for (int k = 0; k < 4; ++k) {
auto [tox,toy] = make_tuple(x + dx[k], y + dy[k]);
ll toh;
// 小心这里,不要漏掉,也不要直接跳过
if (tox < 1 || toy < 1 || tox > N || toy > M) {
toh = 0;
} else {
toh = grid[tox][toy];
}
if (toh < h) {
axis1[k & 1]++;
if (axis1[k & 1] == 2) {
axis1[k & 1] = 0;
axis2[k & 1]++;
}
}
}
ll acc2 = accumulate(all(axis2), 0ll);
ll acc1 = accumulate(all(axis1), 0ll);
ll res = acc2 * 7 + origin_cost[acc1];
return res;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
i128 ans = 0;
for (int x = 1; x <= N; ++x) {
for (int y = 1; y <= M; ++y) {
ll h = grid[x][y];
if (h == 0) {
continue;
}
if (h == 1) {
ans += cal_single(x, y, h, 2);
continue;
}
ans += cal_single(x, y, 1, 1);
ans += cal_single(x, y, h, 1);
vector<ll> hls;
// 注意一下哨兵值的设置
// 如果设定为1的话,那么将无法计入1
hls.push_back(0);
for (int k = 0; k < 4; ++k) {
auto [tox,toy] = make_tuple(x + dx[k], y + dy[k]);
if (tox < 1 || toy < 1 || tox > N || toy > M) continue;
if (grid[tox][toy] == 0) {
continue;
}
if (grid[tox][toy] <= h) {
hls.push_back(grid[tox][toy]);
}
}
hls.push_back(h);
// 不要忘记对HLS进行排序
sort(all(hls));

for (int k = 1; k < SZ(hls); ++k) {
// 采用直接减法方法,所以说要把1给记进去的话,那么哨兵值必须得设为0。
ll num = hls[k] - hls[k - 1];
ans += num * cal_single(x, y, hls[k], 0);
}
ans -= cal_single(x, y, 1, 0);
ans -= cal_single(x, y, h, 0);
}
}
cout << ans << "\n";

AC代码

AC
https://acm.hdu.edu.cn/contest/view-code?cid=1234&rid=11474

心路历程(WA,TLE,MLE……)