专栏文章

题解:P12000 扶苏出勤日记

P12000题解参与者 3已保存评论 3

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
3 条
当前快照
1 份
快照标识符
@mipri48g
此快照首次捕获于
2025/12/03 16:45
3 个月前
此快照最后确认于
2025/12/03 16:45
3 个月前
查看原文
awmc

题目大意

每天赚 bib_i 块钱,11 块钱可以换 aia_i 个币去打乌蒙,求每天打乌蒙的最多次数。

题意分析

显然的二分,那么难点在于考虑 check
check 能想到采用模拟思路,把 nn 天遍历一边,先赚钱,然后换币。如果中途币不够了就 return 0,能过完这 nn 天就 return 1
考虑如何换币:假设 ai<aja_i<a_j (i<j)(i<j),而且第 ii 天的币足以用到第 jj 天,那么显然在第 jj 天换币比在第 ii 天换币更优
更特别的,如果 jjii 最近,那么它们中间的日子换币一定比这两天换币更劣,考虑到这点,使用单调栈维护。
分类讨论,假设每天要打 xx 把,现在是第 ii 天,有 BB 个币, MM 块钱,更优的下一天是 nxtinxt_i
如果当前这一天是之后最优的一天,也就是说 nxtinxt_i 不存在,那么把钱全部花光就好了,反正最优。
那一天存在的话,有 33 种情况:
第一:当前的币已经足够支撑到那一天,即
x×(nxtii)Bx\times(nxt_i-i)\le B
那么一块钱都不用花。
第二:花一部分钱可以使上面的式子成立,也就是说
x×(nxtii)B+M×aix\times(nxt_i-i)\le B+M\times a_i
那么花最少的钱,因为剩下的要留给第 nxtinxt_i 天。
第三:钱花完了也不能使上面的式子成立。
只能在这里把钱全部花完,因为留不到那一天,如果留给中间的日子反而劣,所以最好的是自己用完。
总的就上面几种情况,依次判断即可。

代码&细节

二分上界是 101210^{12}
CPP
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e6+5;
int T,n;
int a[N],b[N],nxt[N];
int kkk;//第4种情况下花的钱

bool chk(int x){
	int now=0,B=0;
	for(int i=1;i<=n;i++){
		now+=b[i];
		int days=nxt[i]-i;
		if(nxt[i]==-1){ //当前最优
			B+=now*a[i];
			now=0;
		}else{
			if(now*a[i]>=x*days-B){ //钱够
				kkk=ceil(1.0*max(x*days-B,0ll)/a[i]); //计算最少需要花的钱
				B+=kkk*a[i];
				now-=kkk;
			}else{ //钱不够
				B+=now*a[i];
				now=0;
			}
		}
		if(B>=x)B-=x;
		else return 0;
	}
	return 1;
}

stack<int> s;
signed main(){
	ios::sync_with_stdio(0);cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++)cin>>a[i];
		for(int i=1;i<=n;i++)cin>>b[i];
		for(int i=1;i<=n;i++){ //维护单调栈
			while(!s.empty()&&a[s.top()]<=a[i]){nxt[s.top()]=i;s.pop();}
			s.push(i);
		}
		while(!s.empty())nxt[s.top()]=-1,s.pop();
		int l=0,r=1e12,mid;
		while(l<r){
			mid=(l+r+1>>1);
			if(chk(mid))l=mid;
			else r=mid-1;
		}
		cout<<l<<'\n';
	}
	return 0;
}
呜呜呜别卡我常

评论

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

正在加载评论...