分析:做这道题之前需要先了解三维前缀和和差分的相关知识,不会三维前缀和的小伙伴可以看下这里:(校赛)星球大战(三维前缀和模板)_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;
}