原题链接:三体攻击
题目描述
三体人将对地球发起攻击。
为了抵御攻击,地球人派出了 $A×B×C$ 艘战舰,在太空中排成一个 $A$ 层 $B$ 行 $C$ 列的立方体。
其中,第 $i$ 层第 $j$ 行第 $k$ 列的战舰(记为战舰 $(i,j,k)$)的生命值为 $d(i,j,k)$。
三体人将会对地球发起 $m$ 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。
具体地,第 $t$ 轮攻击用 $7$ 个参数 $la_t,ra_t,lb_t,rb_t,lc_t,rc_t,h_t$ 描述;
所有满足$ i∈[la_t,ra_t],j∈[lb_t,rb_t],k∈[lc_t,rc_t]$ 的战舰 $(i,j,k)$ 会受到 $h_t$ 的伤害。
如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。
地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。
输入格式
第一行包括 $4$ 个正整数 $A,B,C,m;$
第二行包含 $A×B×C$ 个整数,其中第 $((i−1)×B+(j−1))×C+(k−1)+1$ 个数为 $d(i, j, k);$
第 $3$ 到第 $m+2$ 行中,第 $(t − 2)$ 行包含 $7$ 个正整数 $la_t, ra_t, lb_t, rb_t, lc_t, rc_t, h_t。$
输出格式
输出第一个爆炸的战舰是在哪一轮攻击后爆炸的。
保证一定存在这样的战舰。
数据范围
$1≤A×B×C≤106,$
$1≤m≤106,$
$0≤d(i, j, k), ht≤109,$
$1≤la_t≤ra_t≤A,$
$1≤lb_t≤rb_t≤B,$
$1≤lc_t≤rc_t≤C$
层、行、列的编号都从 $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$ 轮攻击后,战舰 $(1,1,1)$ 总共受到了 $2$ 点伤害,超出其防御力导致爆炸。
二分 + 前缀和 + 差分
这是一道二分 + 前缀和 + 差分的题
因为每一轮造成的伤害结果都是递减的,即血量随着轮数增加而减少,存在单调性,所以可以利用二分找到第一个血量小于零的分界点
对于攻击每一块区域,就是某一块区域内都减去某一个值,这是三维差分
可以推出公式:
$$S_{x,y,z} = b_{x,y,z} + S_{x - 1,y,z} + S_{x,y - 1,z} - S_{x - 1,y - 1,z} + S_{x,y,z - 1} - S_{x - 1,y,z - 1} - S_{x,y - 1,z - 1} + S_{x - 1,y - 1,z - 1}$$
$S$ 是原数组,$b$ 是差分数组
那么可以根据此公式计算出每一块的差分,类似于二维差分,可以对某一个点 $-h(h是伤害值)$ ,其它某些点 $+/-h$
$b_{x,y,z} -= h, b_{x + 1,y, z} += h…$ 可以发现规律,如果 $+1$ 是偶数个,那么就是 $-h$ ,奇数个就是 $+h$ ,总共有$8$个($2^3$)
c++代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2000010;
typedef long long LL;
int A, B, C, m;
LL s[N], b[N], bp[N];
int op[N / 2][7];
const int d[8][4] = {
{0,0,0,1},
{0,0,1,-1},
{0,1,0,-1},
{0,1,1,1},
{1,0,0,-1},
{1,0,1,1},
{1,1,0,1},
{1,1,1,-1}
};
int get(int i,int j,int k) {
return (i * B + j) * C + k;
}
bool check(int mid) {
memcpy(b,bp,sizeof bp);
for(int i = 1;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], h = op[i][6];
b[get(x1, y1, z1)] -= h;
b[get(x1, y1, z2 + 1)] += h;
b[get(x1, y2 + 1, z1)] += h;
b[get(x1, y2 + 1, z2 + 1)] -= h;
b[get(x2 + 1, y1, z1)] += h;
b[get(x2 + 1, y1, z2 + 1)] -= h;
b[get(x2 + 1, y2 + 1, z1)] -= h;
b[get(x2 + 1, y2 + 1, z2 + 1)] += h;
}
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)] = b[get(i,j,k)];
for(int u = 1;u < 8;u ++) {
int x = i - d[u][0], y = j - d[u][1], z = k - d[u][2], t = d[u][3];
s[get(i,j,k)] -= s[get(x,y,z)] * t;
}
if(s[get(i,j,k)] < 0) return true;
}
return false;
}
int main()
{
scanf("%d%d%d%d",&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 ++)
scanf("%lld",&s[get(i,j,k)]);
for(int i = 1;i <= A;i ++)
for(int j = 1;j <= B;j ++)
for(int k = 1;k <= C;k ++)
for(int u = 0;u < 8;u ++) {
int x = i - d[u][0], y = j - d[u][1], z = k - d[u][2], t = d[u][3];
bp[get(i,j,k)] += s[get(x,y,z)] * t;
}
for(int i = 1;i <= m;i ++)
for(int j = 0;j < 7;j ++)
scanf("%d",&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;
}
printf("%d\n",r);
return 0;
}