[蓝桥杯 2018 省 A] 三体攻击
题目描述
三体人将对地球发起攻击。为了抵御攻击,地球人派出了 A × B × C A\times B\times C A×B×C 艘战舰,在太空中排成一个 A A A 层 B B B 行 C C C 列的立方体。其中,第 i i i 层第 j j j 行第 k k k 列的战舰(记为战舰 ( i , j , k ) (i, j, k) (i, j, k))的生命值为 d ( i , j , k ) d(i, j, k) d(i, j, k)。
三体人将会对地球发起 m m m 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。具体地,第 t t t 轮攻击用 7 7 7 个参数 l a t , r a t , l b t , r b t , l c t , r c t , h t la_t, ra_t, lb_t, rb_t, lc_t, rc_t, h_t lat, rat, lbt, rbt, lct, rct, ht 描述;
所有满足 i ∈ [ l a t , r a t ] , j ∈ [ l b t , r b t ] , k ∈ [ l c t , r c t ] 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) 会受到 h t h_t ht 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。
地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。
输入格式
从标准输入读入数据。
第一行包括 4 4 4 个正整数 A A A, B B B, C C C, m m m;
第二行包含 A × B × C A\times B\times C A×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);
第 3 3 3 到第 m + 2 m + 2 m + 2 行中,第 ( t + 2 ) (t + 2) (t + 2) 行包含 7 7 7 个正整数 l a t , r a t , l b t , r b t , l c t , r c t , h t la_t, ra_t, lb_t, rb_t, lc_t, rc_t, h_t lat, 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
提示
【样例解释】
在第 2 2 2 轮攻击后,战舰 ( 1 , 1 , 1 ) (1,1,1) (1,1,1) 总共受到了 2 2 2 点伤害,超出其防御力导致爆炸。
【数据约定】
对于 10 % 10\% 10% 的数据, B = C = 1 B = C = 1 B=C=1;
对于 20 % 20\% 20% 的数据, C = 1 C = 1 C=1;
对于 40 % 40\% 40% 的数据, A × B × C , m ≤ 10000 A\times B \times C, m\le10000 A×B×C, m≤10000;
对于 70 % 70\% 70% 的数据, A , B , C ≤ 200 A, B, C \le 200 A, B, C ≤ 200;
对于所有数据, 1 ≤ A × B × C ≤ 1 0 6 1\le A\times B\times C \le 10^6 1≤A×B×C≤ 106, 1 ≤ m ≤ 1 0 6 1\le m \le 10^6 1≤m≤ 106, 0 ≤ ( i , j , k ) 0 \le (i, j, k) 0≤ (i, j, k), h t ≤ 1 0 9 h_t\le 10^9 ht≤ 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;
}