扶苏的出勤日记(二分+单调队列)

题目链接

赛时想的是二分答案,之后遍历每一天,如果剩下的游戏币能支付就支付,不能就找前面兑换率最大的(ST表或者线段树),用它的前缀和来买,还是不够的话就再找次大值,然后就卡在这了,前缀和变了后面的怎么修改?次大值怎么维护?

既然修改前缀和不行,就加一个记录目前总消费的数,每次用前缀和减去这个数就行了
但是这样不会造成用后面的钱买前面的游戏币吗?

注意到,题目说的是每天的钱可以留到后面使用,所以前面说的,每天可支配的钱是前缀和,这个没问题,但是当当前最大兑换率的前缀和用完后还不够的话,就要找次大值了,而这个次大值只能是后面的最大值,因为前面的钱(前缀和)已经被当前最大兑换率用完了,

是不是很熟悉?每个最大值接上后面的最大值,前面的最大值可能需要删除,不就是单调队列吗?

于是,
1.每次选定L,R进行二分找答案
2.每次找答案都维护一个单调队列,记录每天的前缀和以及兑换率按兑换率来排序
3.维护一个 目前剩余硬币,因为每次剩下来的硬币与其花掉不如带到下一天减少开销
4.如果当天 剩余硬币能满足需要,减去需要,开启下一天;如果不能满足需要,则在单调队列左侧拿钱换硬币,如果不够就一直拿,直到没钱拿了或者能满足需要了
5.如果没钱拿了就说明当前 MID 太多了,R=MID-1
6.如果能满足需要就继续,直到最后都能满足就说明 MID 完全能满足 ,L=MID+1

于是:

#include <bits/stdc++.h>
typedef long long int LL;
using namespace std;
const int N=1e6+10;
LL n,income[N],rate[N],T;

struct DATA
{
	LL rate=0,money=0;
	DATA(){};
	DATA(LL r,LL m):rate(r),money(m){};
}q[N];

bool work(LL MID)
{
	LL nowm=0,nowc=0,havecost=0;
	LL L=1,R=0;
	for(LL i=1;i<=n;i++)
	{
		nowm+=income[i];
		while(L<=R&&q[R].rate<=rate[i]) R--;
		q[++R]=DATA(rate[i],nowm);
		if(nowc<MID)
		{
			while(nowc<MID&&L<=R)
			{
				LL Lrate=q[L].rate,Lm=q[L].money-havecost;
				LL need=MID-nowc;
				LL getm=min((need+Lrate-1)/Lrate,Lm);
				nowc+=getm*Lrate;
				havecost+=getm;
				Lm-=getm;
				if(Lm==0) L++;
				else break;
			}
			if(nowc<MID) return false;
		}
		nowc-=MID;
	}
	return true;
}

int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		LL L=0,R=0,maxr=0;
		memset(rate,0,sizeof(rate));
		memset(income,0,sizeof(income));
		cin>>n;
		for(LL i=1;i<=n;i++) scanf("%lld",&rate[i]),maxr=max(maxr,rate[i]);
		for(LL i=1;i<=n;i++) scanf("%lld",&income[i]),R+=maxr*income[i];
		R/=n;
		L=0;
		while(L<=R)
		{
			LL MID=(L+R)>>1; 
			if(!work(MID))
			{
				R=MID-1;
			}
			else
			{
				L=MID+1;
			}
		}
		printf("%lld\n",R);
	}
	return 0;
}

但这样会TLE,因为数据特别特别大,特别特别多
这种情况一定要scanf和printf,
同时,memset作为保险机制可以不要了,因为只在每次的n内作用,不会出事,直接覆盖就行,带上的话很耗时间!

AC version:

#include <bits/stdc++.h>
typedef long long int LL;
using namespace std;
const int N=1e6+10;
LL n,income[N],rate[N],T;

struct DATA
{
	LL rate=0,money=0;
	DATA(){};
	DATA(LL r,LL m):rate(r),money(m){};
}q[N];

bool work(LL MID)
{
	LL nowm=0,nowc=0,havecost=0;
	LL L=1,R=0;
	for(LL i=1;i<=n;i++)
	{
		nowm+=income[i];
		while(L<=R&&q[R].rate<=rate[i]) R--;
		q[++R]=DATA(rate[i],nowm);
		if(nowc<MID)
		{
			while(nowc<MID&&L<=R)
			{
				LL Lrate=q[L].rate,Lm=q[L].money-havecost;
				LL need=MID-nowc;
				LL getm=min((need+Lrate-1)/Lrate,Lm);
				nowc+=getm*Lrate;
				havecost+=getm;
				Lm-=getm;
				if(Lm==0) L++;
				else break;
			}
			if(nowc<MID) return false;
		}
		nowc-=MID;
	}
	return true;
}

int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		LL L=0,R=0,maxr=0;
		scanf("%lld",&n);
		for(LL i=1;i<=n;i++) scanf("%lld",&rate[i]),maxr=max(maxr,rate[i]);
		for(LL i=1;i<=n;i++) scanf("%lld",&income[i]),R+=maxr*income[i];
		R/=n;
		L=0;
		while(L<=R)
		{
			LL MID=(L+R)>>1; 
			if(!work(MID))
			{
				R=MID-1;
			}
			else
			{
				L=MID+1;
			}
		}
		printf("%lld\n",R);
	}
	return 0;
}
posted @ 2025-03-30 20:01  石磨豆浆  阅读(30)  评论(0)    收藏  举报