蓝桥杯——三体攻击

原题链接:三体攻击

题目描述

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

为了抵御攻击,地球人派出了 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 ((i1)×B+(j1))×C+(k1)+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) (t2) 行包含 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, 1A×B×C106,
1 ≤ m ≤ 106 , 1≤m≤106, 1m106,
0 ≤ d ( i ,   j ,   k ) ,   h t ≤ 109 , 0≤d(i, j, k), ht≤109, 0d(i,j,k),ht109,
1 ≤ l a t ≤ r a t ≤ A , 1≤la_t≤ra_t≤A, 1latratA,
1 ≤ l b t ≤ r b t ≤ B , 1≤lb_t≤rb_t≤B, 1lbtrbtB,
1 ≤ l c t ≤ r c t ≤ C 1≤lc_t≤rc_t≤C 1lctrctC
层、行、列的编号都从 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+Sx1,y,z+Sx,y1,zSx1,y1,z+Sx,y,z1Sx1,y,z1Sx,y1,z1+Sx1,y1,z1

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;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值