[蓝桥杯 2018 省 A] 三体攻击
题目描述
三体人将对地球发起攻击。为了抵御攻击,地球人派出了 A×B×CA\times B\times CA×B×C 艘战舰,在太空中排成一个 AAA 层 BBB 行 CCC 列的立方体。其中,第 iii 层第 jjj 行第 kkk 列的战舰(记为战舰 (i, j, k)(i, j, k)(i, j, k))的生命值为 d(i, j, k)d(i, j, k)d(i, j, k)。
三体人将会对地球发起 mmm 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。具体地,第 ttt 轮攻击用 777 个参数 lat, rat, lbt, rbt, lct, rct, htla_t, ra_t, lb_t, rb_t, lc_t, rc_t, h_tlat, rat, lbt, rbt, lct, rct, ht 描述;
所有满足 i∈[lat, rat],j∈[lbt, rbt],k∈[lct, rct]i\in [la_t, ra_t],j\in [lb_t, rb_t],k\in [lc_t, rc_t]i∈[lat, rat],j∈[lbt, rbt],k∈[lct, rct] 的战舰 (i, j, k)(i, j, k)(i, j, k) 会受到 hth_tht 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。
地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。
输入格式
从标准输入读入数据。
第一行包括 444 个正整数 AAA,BBB,CCC,mmm;
第二行包含 A×B×CA\times B\times CA×B×C 个整数,其中第 ((i − 1)×B+(j − 1))×C+(k − 1)+1((i − 1)\times B + (j − 1)) \times C + (k − 1)+1((i − 1)×B+(j − 1))×C+(k − 1)+1 个数为 d(i, j, k)d(i, j, k)d(i, j, k);
第 333 到第 m + 2m + 2m + 2 行中,第 (t + 2)(t + 2)(t + 2) 行包含 777 个正整数 lat, rat, lbt, rbt, lct, rct, htla_t, ra_t, lb_t, rb_t, lc_t, rc_t, h_tlat, rat, lbt, rbt, lct, rct, ht。
输出格式
输出到标准输出。
输出第一个爆炸的战舰是在哪一轮攻击后爆炸的。保证一定存在这样的战舰。
样例 #1
样例输入 #1
2 2 2 3
1 1 1 1 1 1 1 1
1 2 1 2 1 1 1
1 1 1 2 1 2 1
1 1 1 1 1 1 2
样例输出 #1
2
提示
【样例解释】
在第 222 轮攻击后,战舰 (1,1,1)(1,1,1)(1,1,1) 总共受到了 222 点伤害,超出其防御力导致爆炸。
【数据约定】
对于 10%10\%10% 的数据,B=C=1B = C = 1B=C=1;
对于 20%20\%20% 的数据,C=1C = 1C=1;
对于 40%40\%40% 的数据,A×B×C, m≤10000A\times B \times C, m\le10000A×B×C, m≤10000;
对于 70%70\%70% 的数据,A, B, C ≤ 200A, B, C \le 200A, B, C ≤ 200;
对于所有数据,1≤A×B×C≤ 1061\le A\times B\times C \le 10^61≤A×B×C≤ 106,1≤m≤ 1061\le m \le 10^61≤m≤ 106,0≤ (i, j, k)0 \le (i, j, k)0≤ (i, j, k), ht≤ 109h_t\le 10^9ht≤ 109。
思路
三维差分
【差分数组求法】
// 在考虑差分数组的时候考虑规律更容易理解一点
// (x1, y1, z1)和(x2, y2, z2)之间的原序列都加上c,对数组b的影响:
// 偶数个1是加的话那么奇数个1就减 下方代码中的add和sub函数就是如此
b[x1 ][y1 ][z1 ] += c; // 000
b[x1 ][y1 ][z2 + 1] -= c; // 001
b[x1 ][y2 + 1][z1 ] -= c; // 010
b[x1 ][y2 + 1][z2 + 1] += c; // 011
b[x2 + 1][y1 ][z1 ] -= c; // 100
b[x2 + 1][y1 ][z2 + 1] += c; // 101
b[x2 + 1][y2 + 1][z1 ] += c; // 110
b[x2 + 1][y2 + 1][z2 + 1] -= c; // 111
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 2e6 + 10;
int A, B, C, m; //层,行,列
int s[N], b[N], op[N / 2][7], tmp[N]; //前缀和,差分数组,操作数,
int get(int i, int j, int k) {
return (i * B + j) * C + k;
}
//求解差分数组函数
void add(int x1, int x2, int y1, int y2, int z1, int z2, int c) {
b[get(x1, y1, z1)] += c;
b[get(x1, y1, z2 + 1)] -= c;
b[get(x1, y2 + 1, z1)] -= c;
b[get(x1, y2 + 1, z2 + 1)] += c;
b[get(x2 + 1, y1, z1)] -= c;
b[get(x2 + 1, y1, z2 + 1)] += c;
b[get(x2 + 1, y2 + 1, z1)] += c;
b[get(x2 + 1, y2 + 1, z2 + 1)] -= c;
}
//进行攻击函数
void sub(int x1, int x2, int y1, int y2, int z1, int z2, int c) {
tmp[get(x1, y1, z1)] -= c;
tmp[get(x1, y1, z2 + 1)] += c;
tmp[get(x1, y2 + 1, z1)] += c;
tmp[get(x1, y2 + 1, z2 + 1)] -= c;
tmp[get(x2 + 1, y1, z1)] += c;
tmp[get(x2 + 1, y1, z2 + 1)] -= c;
tmp[get(x2 + 1, y2 + 1, z1)] -= c;
tmp[get(x2 + 1, y2 + 1, z2 + 1)] += c;
}
bool check(int mid) {
//将tmp数组初始化为b数组
memcpy(tmp, b, sizeof b);
//进行攻击
for (int i = 0; i < mid; i++) {
int x1 = op[i][0], x2 = op[i][1], y1 = op[i][2], y2 = op[i][3],
z1 = op[i][4], z2 = op[i][5], c = op[i][6];
sub(x1, x2, y1, y2, z1, z2, c);
}
memset(s, 0, sizeof s);
for (int i = 1; i <= A; i++)
for (int j = 1; j <= B; j++)
for (int k = 1; k <= C; k++) {
s[get(i, j, k)] = tmp[get(i, j, k)] +
s[get(i - 1, j, k)] + s[get(i, j - 1, k)] - s[get(i - 1, j - 1, k)]
+ s[get(i, j, k - 1)] - s[get(i - 1, j, k - 1)] - s[get(i, j - 1, k - 1)]
+ s[get(i - 1, j - 1, k - 1)];
if (s[get(i, j, k)] < 0)
return true;
}
return false;
}
int main() {
cin >> A >> B >> C >> m;
for (int i = 1; i <= A; i++)
for (int j = 1; j <= B; j++)
for (int k = 1; k <= C; k++) {
cin >> s[get(i, j, k)];
//创建差分数组
add(i, i, j, j, k, k, s[get(i, j, k)]);
}
//读入操作数组
for (int i = 0; i < m; i++)
for (int j = 0; j < 7; j++)
cin >> op[i][j];
//二分答案
int l = 1, r = m;
while (l < r) {
int mid = l + r >> 1;
if (check(mid))
r = mid;
else
l = mid + 1;
}
cout << l << endl;
return 0;
}