题目链接
题目大意:
小明在游戏中搭建了一堵长为的城墙,墙上有个支撑点。为了知道墙是否足够坚固,小明喊来他的好朋友小刚帮助他进行测试。
小刚有一种特殊的炮弹可以对墙上任意一个支撑点进行轰击,收到轰击的支撑点将受到点伤害,此外,炮弹还会对个支撑点造成溅射伤害(受到的伤害依次减1)。
现在知道每个支撑点能够承受的最大伤害,1<=n,m<=2^5当时视为该支撑点已经完全损坏。小刚一共进行轮炮击,在炮击结束之后请你计算出一共有多少个支撑点被完全损坏。
题目思路:
\(~~\)我们通过写两组样例来观察每次造成伤害的效果
10 2
10 10 10 10 10 10 10 10 10 10
5 4
1 10
第一次我们对第五个点造成4点伤害,同时对周围造成溅射伤害:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 3 | 2 | 1 | 0 | 0 |
我们发现每次伤害的变化的间隔都是相同的,我们就考虑能不能优化存储这种变化,于是就先考虑能不能对其进行差分,最后通过前缀和来计算对某个点的伤害
于是我们得到第一次差分后的结果:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 0 |
我们惊奇的发现第一次差分后的结果依然是一种间隔相同的变化,那这就意味着可以进行第二次差分:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | -1 -1 | 0 | 0 | 0 | 1 |
经过这样的变化之后我们发现二次差分后的结果更便于处理:get新技能
只需要简单的处理二次差分的伤害,最后通过两次前缀和就可以求出某一点最终受到的伤害值
代码实现
# include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+10;
int a[N];
int b[N],c[N];
int t[N];
signed main(){
int n,m;
cin>>n>>m;
for(int i = 1;i <= n;++i){
cin>>a[i];
}
for(int i = 1;i <= n;++i) b[i] =a[i]- a[i-1];
for(int i = 1;i <= n;++i) c[i] = b[i]- b[i-1];
while(m--){
int p,x;
cin>>p>>x;
if(x == 1){
c[p] -= 1;
c[p+1] +=1;
c[p+1] += 1;
c[p+2] -= 1;
continue;
}
if(p-x+1>=1){
c[p-x+1] -= 1;
c[p+1] +=1;
}
else if(p == 1){
c[p] -= x;
c[p+1] +=x;
}
else{
int t = x-(p-1);
c[1] -= t;
c[2] += t;
c[2] -= 1;
c[p+1] +=1;
}
c[p+1] += 1;
c[p+x+1] -= 1;
}
for(int i = 1;i <= n;++i) b[i] = b[i-1]+c[i];
for(int i = 1;i <= n;++i){
a[i] = a[i-1]+b[i];
}
vector<int> p;
for(int i = 1;i <= n;++i){
if(a[i]<=0){
p.push_back(i);
}
}
cout<<p.size()<<endl;
for(auto it:p) cout<<it<<" ";
cout<<endl;
return 0;
}
不过在处理的时候要注意边界,有些伤害不一定完全打完了,可能5的伤害扩散到边界的时候还剩下2
这个时候就需要特殊处理一下了