专栏文章

题解: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 个月前
查看原文
我不会告诉你这道题比第五题难。
首先,暴力模拟肯定不行,因为时间复杂度会达到 O(nq)\mathcal O(nq),必然超时。而我们发现 ppaabb 数组最大都只有五百,而 nn 也只有一万,这启示我们用 O(np)\mathcal O(np) 的算法。
再观察一下,我们会发现如果第 ii 次之前的心情小于 pip_i,那么他之后的心情永远不会超过一千,因为如果他的心情超过 pip_i (之后我们暂时默认 pip_i 都为五百),那他的心情一定会疯狂下降,直到跌入五百大关。之后心情会上升,但最大情况下,此时心情为五百,aia_i 也为五百,那它的心情达到一千,之后必然会掉,因为这必然会超过 pi+1p_{i+1}。接下来回到全局,如果高桥的初始心情超过 p1p_1,那他的心情就会一直掉,直到掉进五百大关,再遵循情况一,心情在零到一千间上下起伏。所以我们把情况分成两部分:一直掉的阶段,上下起伏的阶段。
上下起伏的阶段容易实现。我们设 dpijdp_{i_j} 为从第 ii 个礼物开始拿,一直拿到最后一个。而拿之前的心情为 jj 时的最终心情。递推式极其简单:当 jpij \le p_i 时,dpij=dpi+1j+aidp_{i_j}=dp_{i+1_{j+a_i}},因为他拿了这个后,相当于从第 i+1i+1 个礼物开始拿,此时心情为 j+aij+a_i 的最终心情;否则 j>pij>p_i 时,dpij=dpi+1max(0,jbi)dp_{i_j}=dp_{i+1_{\max(0,j-b_i)}},因为他拿了这个后,相当于从第 i+1i+1 个礼物开始拿,此时心情为 max(0,jbi)\max(0,j-b_i) 的最终心情(因为心情是要大于等于零的)。
至于初始状态,把所有 dpn+1idp_{n+1_i} 的位置初始化成 ii 即可。
接下来我们看一直下降的阶段。在此之前,我们设 si=1ibis_i=\sum_1^i b_i,也就是前缀和。而我们要找的就是第一个 xsi1pix-s_{i-1} \le p_i 的位置。因为从 ii 点开始(包括这个点),它就进入第二阶段了,结果就是刚才预处理出的结果了。不过显然暴力不行;而看到这种,你可能第一时间想到二分,然而很遗憾,这玩意不满足单调性。不过好在 sis_i 不会太大,我们可以换一种思路。
首先,我们设 fif_i 为满足 sj1+pj=is_{j-1}+p_j=i 中最小的 jj 的值。这很容易预处理;然后我们对 fif_i 从后往前做一次后缀最小值,然后 fif_i 的值就变成了 sj1+pjis_{j-1}+p_j \ge i 中最小的 jj 的值。而在查询的循环中原先找到的那个 ii,就可以替换成 fxf_x 了,时间复杂度一下子降到了 O(1)\mathcal O(1)
还有,如果 xx 减去前 n1n-1bib_i 还大于 pnp_n,就意味着没有经历第二阶段,要特判输出。
代码:
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;
}
时间复杂度可以认为是预处理 O(n)\mathcal O(n) 乘一个一千左右的大常数;查询 O(q)\mathcal O(q)。反正能过。
最后,再次无耻地求过。

评论

1 条评论,欢迎与作者交流。

正在加载评论...