(蓝桥杯)三体攻击(三维差分+前缀和+二分)

题目链接:1232. 三体攻击 - AcWing题库

 分析:做这道题之前需要先了解三维前缀和和差分的相关知识,不会三维前缀和的小伙伴可以看下这里:(校赛)星球大战(三维前缀和模板)_AC__dream的博客-CSDN博客

三维差分和二维差分是类似的,先说一下二维差分,假如我们想让(x1,y1)与(x2,y2)之间的点的值都加k,那么我们可以令(由容斥原理得到)

d[x1][y1]+=k;

d[x1][y2+1]-=k;

d[x2+1][y1]-=k;

d[x2+1][y2+1]+=k;

对二维差分求前缀和即得到原数组

对于三维,假如我们想让(x1,y1,z1)与(x2,y2,z2)之间的点的值都加k,那么我们可以令

d[x1][y1][z1]+=k;

d[x2+1][y1][z1]-=k;

d[x1][y2+1][z1]-=k;

d[x1][y1][z2+1]-=k;

d[x2+1][y2+1][z1]+=k;

d[x2+1][y1][z2+1]+=k;

d[x1][y2+1][z2+1]+=k;

d[x2+1][y2+1][z2+1]-=k;

同理,对三维差分求前缀和即可得到原三维数组。

有了前缀和差分知识,我们可以对这道题目进行分析了。

刚开始看到这道题有没有一种想直接暴力解决的想法?

这显然是不行的,我一开始的思路就是用差分o(1)修改代替暴力o(n^2)修改,然后再一个一个判断是否会有战舰爆炸,但是这样依旧会超时,仔细想想我们可以发现,假如第x轮攻击后有战舰爆炸,那么第x+1轮攻击后肯定也会有战舰爆炸,因为对于每次攻击,战舰受到的伤害只能增加而不能减少,所以说战舰受到的伤害具有单调性,所以我们就可以二分战舰受到的攻击次数,这样我们就能够解决本题了

对了,还有一点需要注意,就是本题只能开一维数组,所以我们应该写一个坐标转换公式,这样我们就可以安心写表达式而不用思考怎么转换坐标的问题了,不容易出错

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=2e6+10;
ll t[N],d[N],s[N];
struct node{
	int la,ra,lb,rb,lc,rc,h;
}p[N];
ll a,b,c,m;
ll find(int i,int j,int k)
{
	return (ll)(i-1)*b*c+(j-1)*c+k;
}
bool check(int x)
{
	memset(d,0,sizeof d);
	for(int i=1;i<=x;i++)
	{
		d[find(p[i].la,p[i].lb,p[i].lc)]-=p[i].h;
		d[find(p[i].ra+1,p[i].lb,p[i].lc)]+=p[i].h;
		d[find(p[i].la,p[i].rb+1,p[i].lc)]+=p[i].h;
		d[find(p[i].la,p[i].lb,p[i].rc+1)]+=p[i].h;
		d[find(p[i].ra+1,p[i].rb+1,p[i].lc)]-=p[i].h;
		d[find(p[i].ra+1,p[i].lb,p[i].rc+1)]-=p[i].h;
		d[find(p[i].la,p[i].rb+1,p[i].rc+1)]-=p[i].h;
		d[find(p[i].ra+1,p[i].rb+1,p[i].rc+1)]+=p[i].h;
	}
	for(int i=1;i<=a;i++)
	for(int j=1;j<=b;j++)
	for(int k=1;k<=c;k++)
	{
		d[find(i,j,k)]+=d[find(i-1,j,k)]+d[find(i,j-1,k)]+d[find(i,j,k-1)]-d[find(i-1,j-1,k)]-d[find(i-1,j,k-1)]-d[find(i,j-1,k-1)]+d[find(i-1,j-1,k-1)];
		if(-d[find(i,j,k)]>t[find(i,j,k)]) 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++)
		scanf("%lld",&t[find(i,j,k)]);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d%d%d%d%d",&p[i].la,&p[i].ra,&p[i].lb,&p[i].rb,&p[i].lc,&p[i].rc,&p[i].h);
	ll l=1,r=m;
	while(l<r)
	{
		ll mid=l+r>>1;
		if(check(mid)) r=mid;//有战舰爆炸的情况 
		else l=mid+1; 
	}
	printf("%lld",l);
	return 0;
} 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值