[蓝桥杯 2018 省 A] 三体攻击

[蓝桥杯 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,m10000

对于 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 1A×B×C 106 1 ≤ m ≤   1 0 6 1\le m \le 10^6 1m 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值