专栏文章
题解:AT_abc417_d [ABC417D] Takahashi's Expectation
AT_abc417_d题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @miojpr85
- 此快照首次捕获于
- 2025/12/02 20:19 3 个月前
- 此快照最后确认于
- 2025/12/02 20:19 3 个月前
首先,暴力模拟肯定不行,因为时间复杂度会达到 ,必然超时。而我们发现 、、 数组最大都只有五百,而 也只有一万,这启示我们用 的算法。
再观察一下,我们会发现如果第 次之前的心情小于 ,那么他之后的心情永远不会超过一千,因为如果他的心情超过 (之后我们暂时默认 都为五百),那他的心情一定会疯狂下降,直到跌入五百大关。之后心情会上升,但最大情况下,此时心情为五百, 也为五百,那它的心情达到一千,之后必然会掉,因为这必然会超过 。接下来回到全局,如果高桥的初始心情超过 ,那他的心情就会一直掉,直到掉进五百大关,再遵循情况一,心情在零到一千间上下起伏。所以我们把情况分成两部分:一直掉的阶段,上下起伏的阶段。
上下起伏的阶段容易实现。我们设 为从第 个礼物开始拿,一直拿到最后一个。而拿之前的心情为 时的最终心情。递推式极其简单:当 时,,因为他拿了这个后,相当于从第 个礼物开始拿,此时心情为 的最终心情;否则 时,,因为他拿了这个后,相当于从第 个礼物开始拿,此时心情为 的最终心情(因为心情是要大于等于零的)。
至于初始状态,把所有 的位置初始化成 即可。
接下来我们看一直下降的阶段。在此之前,我们设 ,也就是前缀和。而我们要找的就是第一个 的位置。因为从 点开始(包括这个点),它就进入第二阶段了,结果就是刚才预处理出的结果了。不过显然暴力不行;而看到这种,你可能第一时间想到二分,然而很遗憾,这玩意不满足单调性。不过好在 不会太大,我们可以换一种思路。
首先,我们设 为满足 中最小的 的值。这很容易预处理;然后我们对 从后往前做一次后缀最小值,然后 的值就变成了 中最小的 的值。而在查询的循环中原先找到的那个 ,就可以替换成 了,时间复杂度一下子降到了 。
还有,如果 减去前 个 还大于 ,就意味着没有经历第二阶段,要特判输出。
代码:
CPP#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N=1e4+10,M=1000;
//dp_i_j 从第i个物品开始拿,此时心情为j的最终值
int p[N],a[N],b[N],s[N],dp[N][M+10],f[N*505+10]; //那些数组
signed main(){
ios::sync_with_stdio(0); //快读
cin.tie(0),cout.tie(0);
int n,q;
cin>>n;
for(int i=0;i<=n*505+1;i++) f[i]=1e18; //初始化成最大值
for(int i=1;i<=n;i++){
cin>>p[i]>>a[i]>>b[i];
s[i]=s[i-1]+b[i];
f[s[i-1]+p[i]]=min(f[s[i-1]+p[i]],i); //初始化f数组
}
for(int i=n*505;i>=0;i--) f[i]=min(f[i],f[i+1]); //后缀最小值
for(int j=1;j<=M;j++) dp[n+1][j]=j; //dp预处理
for(int i=n;i>=1;i--){
for(int j=0;j<=M;j++){
if(j<=p[i]) dp[i][j]=dp[i+1][j+a[i]];
else dp[i][j]=dp[i+1][max(0ll,j-b[i])]; //两种情况
}
}
cin>>q;
while(q--){
int x;
cin>>x;
if(x-s[n-1]>p[n]) cout<<max(0ll,x-s[n])<<endl; //特判
else cout<<dp[f[x]][max(0ll,x-s[f[x]-1])]<<endl; //输出答案
}
//n<=p_x+s_x
return 0;
}
时间复杂度可以认为是预处理 乘一个一千左右的大常数;查询 。反正能过。
最后,再次无耻地求过。
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...