原题链接:三体攻击
题目描述
三体人将对地球发起攻击。
为了抵御攻击,地球人派出了 A × B × C A×B×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∈[la_t,ra_t],j∈[lb_t,rb_t],k∈[lc_t,rc_t]$ 的战舰 ( i , j , k ) (i,j,k) (i,j,k) 会受到 h t h_t ht 的伤害。
如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。
地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。
输入格式
第一行包括 4 4 4 个正整数 A , B , C , m ; A,B,C,m; A,B,C,m;
第二行包含 A × B × C A×B×C A×B×C 个整数,其中第 ( ( i − 1 ) × B + ( j − 1 ) ) × C + ( k − 1 ) + 1 ((i−1)×B+(j−1))×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
≤
A
×
B
×
C
≤
106
,
1≤A×B×C≤106,
1≤A×B×C≤106,
1
≤
m
≤
106
,
1≤m≤106,
1≤m≤106,
0
≤
d
(
i
,
j
,
k
)
,
h
t
≤
109
,
0≤d(i, j, k), ht≤109,
0≤d(i, j, k), ht≤109,
1
≤
l
a
t
≤
r
a
t
≤
A
,
1≤la_t≤ra_t≤A,
1≤lat≤rat≤A,
1
≤
l
b
t
≤
r
b
t
≤
B
,
1≤lb_t≤rb_t≤B,
1≤lbt≤rbt≤B,
1
≤
l
c
t
≤
r
c
t
≤
C
1≤lc_t≤rc_t≤C
1≤lct≤rct≤C
层、行、列的编号都从
1
1
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 点伤害,超出其防御力导致爆炸。
二分 + 前缀和 + 差分
这是一道二分 + 前缀和 + 差分的题
因为每一轮造成的伤害结果都是递减的,即血量随着轮数增加而减少,存在单调性,所以可以利用二分找到第一个血量小于零的分界点
对于攻击每一块区域,就是某一块区域内都减去某一个值,这是三维差分
可以推出公式:
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_{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} Sx,y,z=bx,y,z+Sx−1,y,z+Sx,y−1,z−Sx−1,y−1,z+Sx,y,z−1−Sx−1,y,z−1−Sx,y−1,z−1+Sx−1,y−1,z−1
S S S 是原数组, b b b 是差分数组
那么可以根据此公式计算出每一块的差分,类似于二维差分,可以对某一个点 − h ( h 是 伤 害 值 ) -h(h是伤害值) −h(h是伤害值) ,其它某些点 + / − h +/-h +/−h
b x , y , z − = h , b x + 1 , y , z + = h . . . b_{x,y,z} -= h, b_{x + 1,y, z} += h... bx,y,z−=h,bx+1,y,z+=h... 可以发现规律,如果 + 1 +1 +1 是偶数个,那么就是 − h -h −h ,奇数个就是 + h +h +h ,总共有 8 8 8个( 2 3 2^3 23)
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;
}