[ABC388D] Coming of Age Celebration 题解
注:2024.1.16 修改了一些表达上的错误
题目大意
有 $N$ 个外星人,一开始他们有 $A_i$ 个石头,第 $i$ 个外星人经过 $i$ 年后成年,同时得到其他已经成年的外星人每人一个石头,且每个人最少有 $0$ 个石头。求 $N$ 年后每个外星人的石头数量。
思路
暴力
我们按照年份从 $1 \sim n$ 遍历一次,当一个外星人成年时,对前面的人的石头数进行更新,时间复杂度:$O(n^2)$。
正解
通过前面的暴力,我们发现每次更新前面的石头时,除了已经没有石头的人之外,每人都需要减少一个石头,这里是重复的,有没有更好的办法呢?
我们逆向思维一下,一个人成年后会先得到前面的人的石头,再给后面的人每人一个石头,假设我们已经求出这个人得到石头后的数量,记为 $x$,则第 $i$ 个人最终的石头数 $ans = \max(0,x-(n-i))$。
进一步思考,当 $x \ge n-i$ 时,意味着这个人有足够的石头给后面所有人;而当 $x < n-i$ 时,意味着这个人只能给他后面 $x$ 个人石头。并且这个区间连续,于是我们可以先给下一个做一个标记,等到遍历时,再加上,这也就是我们常说的差分。
实现
先构造好差分数组 $b$ ,第一种情况下,直接将 $b_{i+1}$ 加上一即可,而第二种情况下,还需要将 $b_{i+x+1}$ 减去一。
代码参考
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+10;
int n,a[N],b[N];
int main(){
cin>>n;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i) b[i]=a[i]-a[i-1];//构造差分数组
for(int i=1;i<=n;++i){
a[i]=b[i]+a[i-1];//先拿走前面的人的石头
int ans=a[i]-n+i;//减去给后面的人的石头
b[i+1]++;
if(ans<0){
b[i+a[i]+1]--;
cout<<0<<' ';
}
else cout<<ans<<' ';
}
return 0;
}

浙公网安备 33010602011771号