[ABC417D] Takahashi's Expectation
题面- [ABC417D] Takahashi's Expectation
算法关键词:DP,思维题
题目大意:初始一个情绪值 \(X\),依次收到 \(N\) 个礼物,每个礼物分别有自己的价值 \(P_i\) ,升值 \(A_i\) ,降值 \(B_i\)。若情绪值高于价值则 把情绪值加上升值 ,否则 把情绪值减去降值。现在给出 \(Q\) 个 \(X_i\),分别问收完礼物后的情绪值。
思路1:暴力
直接对每一个情绪值分别跑一遍模拟,复杂度 \(O(NQ)\)。在
\(1 \le N \le 10000\),\(1 \le Q \le 5 \times 10^5\)
的数据范围下显然难以通过。
思路2
发现对于每一个情绪值,礼物都是相同的,考虑能不能初始化收礼物的过程,期望复杂度 \(O(N+Q)\)。
继续思考,发现对于每一个期望值,礼物造成的效果都不同,没法初始化。
太悲伤了啥时候才能做出来TwT。
正常思考似乎没有突破口,既然卡在时间复杂度上,那我们不妨从数据范围入手。
思路3(正解)
- \(1 \le P_i \le 500\,(1 \le i \le N)\)
- \(1 \le A_i \le 500\,(1 \le i \le N)\)
- \(1 \le B_i \le 500\,(1 \le i \le N)\)
注意到这个 \(P\),\(A\),\(B\) 的数据范围格外小,只有 \(500\),一看就是本题的突破点。但是不影响我没做出来。 但是只关注到这点似乎不够做出这道题,那我们不妨观察一下样例。
样例一,没看出来什么,似乎就是一个普通小样例。
样例二,情绪值非常大!所以一直在减少。由此可以发现当情绪值足够大的时候 无论收到什么礼物都是减少情绪值。
非常大的情绪值很好说,一直减即可。但是如果减到一半需要开始判断了怎么办呢AwA?那么我们可以找一找什么时候开始不再是一直减了。
随便举一个较小的例子,发现一个重要的规律:
如果情绪值 \(\le 1000\),那么情绪值就会一直保持在\(1000\)以下。
原因也很简单,如果情绪值 \(\le 500\),那么最大情况下情绪值也不会超过 \(1000\)(理由数据范围);如果情绪值 \(\ge 500\)且 \(\le 1000\),那么情绪值一定会减少且最多减少到 \(500\)。真是一个伟大的发现!
那么二分一下情绪值进入 \(1000\) 减的次数即可,现在范围已经缩小到了\(1000\)。
剩下的DP就很显而易见了 (额其实我是看了算法标签才后知后觉)。
状态表示:$dp_{i,j} $ 表示从第 \(i\) 个礼物开始收(进入\(1000\)范围),刚开始情绪值为 \(j\) 的最终情绪值。
转移顺序:\(i\) 从 \(n\) 到 \(1\) ,\(j\) 从 \(1\) 到 \(1000\) 枚举(注意不是正序,因为并不是从一开始就在范围里没法正着推)。
转移方程:
如果\(j>P_i\),那么\(dp_{i,j}=dp_{i+1,max(0,j-b_j)}\)。否则\(dp_{i,j}=dp_{i+1,j+a_i}\)。
那么到这里就完整搞完了!接下来就是代码部分了。
#include <bits/stdc++.h>
using namespace std;
const int N=10010;
int p[N],a[N],b[N];
int main (){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i]>>a[i]>>b[i];
}
for(int i=0;i<=1000;i++){
dp[n+1][j]=j;
}
for(int i=n;i>=1;i--){
for(int j=0;j<=1000;j++){
dp[i][j]=dp[i+1][j>p[i]?max(0,j-b[i]):j+a[i]];
}
}
for(int i=1;i<=n;i++){
s[i]=s[i-1]+b[i];
}
int q;
cin>>q;
while(q--){
int x;
cin>>x;
if(x-1000>s[n]){
cout<<x-s[n]<<endl;
continue;
}
if(x<=1000){
cout<<dp[1][x]<<endl;
continue;
}
int id=lower_bound(s+1,s+n+1,x-1000)-s;
cout<<dp[id+1][x-s[id]]<<endl;
}
return 0;
}