P1046 抢修计划 官方题解
Gioush OJ · P1046 抢修计划
抢修计划
【题意简述】
有 个依次开放的区域。区域 始终可以抢修;只有区域 均至少抢修过一次,区域 才能被抢修。首次抢修区域 获得 ,以后每次获得 。最多进行 次行动,求最大总价值。
【Hint】
【提示】
假设最终只开放到区域 。前 个区域必须各完成一次首次抢修,剩余行动全部放在 最大的已开放区域上。
【数据点 1∼51\sim 51∼5】
当 时,可以把一次抢修看成搜索树的一层。状态记录各区域已经抢修的次数,枚举当前已经开放的区域作为下一次选择。同一状态只保留已经得到的最大价值,搜索深度不超过 。
这一做法完整枚举了全部合法行动序列,可以作为后续结论的直接验证。
【数据点 6∼106\sim 106∼10】
假设最后开放到区域 。区域 都必须完成一次首次抢修,所以已经消耗 次行动,得到
剩余 次行动不会再改变开放范围。每次都选择 最大的已开放区域一定最优,因此候选答案为
直接枚举 并重新计算前缀最大值,时间复杂度为 。
【数据点 11∼1511\sim 1511∼15】
特殊性质保证 单调不降。最后开放到区域 时,已经开放区域中的最大重复抢修价值就是 ,于是候选答案变为
顺序维护 的前缀和即可在线性时间完成这一档。
【正解】
定义
最后开放到区域 时,最优价值为
其中 。
必要性是显然的:想要开放区域 ,必须依次完成前 个区域的首次抢修,因此前 次行动的总价值固定为 ;剩余每次行动的价值不超过 。
充分性同样成立:依次完成前 个区域的首次抢修,再把剩余 次行动全部放在某个取得 的区域上,恰好得到上述价值。因此枚举 已经覆盖全部最优方案。
顺序扫描时同时维护 ,对每个合法的 更新答案即可。
【复杂度分析】
时间复杂度为 ,空间复杂度为 。总价值需要使用 long long。
【参考代码】
/*Author:EhundateghDate:2026/7/30Name:repair.cppYou steal,I kill.*/#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 200010using namespace std; int c,T,n,k;long long a[MAXN],b[MAXN]; void Solve(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); for(int i=1;i<=n;i++) scanf("%lld",&b[i]); long long Ans=0,PreSum=0,Max=0; for(int i=1;i<=min(n,k);i++){ PreSum+=a[i]; Max=max(Max,b[i]); Ans=max(Ans,PreSum+1ll*(k-i)*Max); } printf("%lld\n",Ans);} int main(){ scanf("%d%d",&c,&T); while(T-->0) Solve(); return 0;}