三体攻击(3维前缀和)


三体攻击

三体人将对地球发起攻击。

为了抵御攻击,地球人派出了 A×B×C 艘战舰,在太空中排成一个 A 层 B 行 C 列的立方体。

其中,第 i 层第 j 行第 k 列的战舰(记为战舰 (i,j,k))的生命值为 d(i,j,k)。

三体人将会对地球发起 m 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。

具体地,第 t 轮攻击用 7 个参数 lat,rat,lbt,rbt,lct,rct,ht 描述;

所有满足 i∈[lat,rat],j∈[lbt,rbt],k∈[lct,rct] 的战舰 (i,j,k) 会受到 ht 的伤害。

如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。

地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。

输入格式
第一行包括 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 个正整数 lat, rat, lbt, rbt, lct, rct, ht。

输出格式
输出第一个爆炸的战舰是在哪一轮攻击后爆炸的。

保证一定存在这样的战舰。

数据范围
1≤A×B×C≤106,
1≤m≤106,
0≤d(i, j, k), ht≤109,
1≤lat≤rat≤A,
1≤lbt≤rbt≤B,
1≤lct≤rct≤C
层、行、列的编号都从 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
输出样例:
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)
三维差分操作 给(x1,y1,z1) 到 (x2,y2,z2) 加 h,插入操作 :

void insert(LL b[],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;
}

ac代码:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 2000010;
int A,B,C,m;
LL s[N],b[N],bp[N];
struct Op
{
    int x1,x2,y1,y2,z1,z2,h;
}op[N];

int get(int i,int j,int k)
{
    return ((i-1)*B+(j-1))*C+k;
}
void insert(LL b[],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;
}
bool check(int t)
{
    memcpy(bp,b,sizeof(bp));
    for(int i=1; i<=t; ++i){ //操作
        insert(bp,op[i].x1,op[i].x2,op[i].y1,op[i].y2,op[i].z1,op[i].z2,op[i].h);
    }
    for(int i=1; i<=A; ++i){
        for(int j=1; j<=B; ++j){
            for(int k=1; k<=C; ++k){ //三维前缀和
                bp[get(i,j,k)]+=bp[get(i-1, j,   k)];
                bp[get(i,j,k)]+=bp[get(i,   j-1, k)];
                bp[get(i,j,k)]-=bp[get(i-1, j-1, k)];
                bp[get(i,j,k)]+=bp[get(i,   j,   k-1)];
                bp[get(i,j,k)]-=bp[get(i-1, j,   k-1)];
                bp[get(i,j,k)]-=bp[get(i,   j-1, k-1)];
                bp[get(i,j,k)]+=bp[get(i-1, j-1, k-1)];
                if(bp[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)]);
                insert(b,i,i,j,j,k,k,s[get(i,j,k)]);
            }
        }
    }
    for(int i=1; i<=m; ++i)
    {
        int x1,x2,y1,y2,z1,z2,h;
        scanf("%d%d%d%d%d%d%d", &x1,&x2,&y1,&y2,&z1,&z2,&h);
        op[i] = {x1,x2,y1,y2,z1,z2,-h};
    }
    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",l);
    return 0;
}

文章作者: 葛济维
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 葛济维 !
评论
  目录