[Codeforces1238G]Adilbek and the Watering System

题意

一个容量为 c c c升的浇水机器,一开始有 c 0 c0 c0升水,它每分钟会消耗 1 1 1升水

n n n个人,第 i i i个人会在第 t i t_i ti分钟至多带 a i a_i ai升水过来并把他带的水一次性倒进机器,每升水卖 b i b_i bi

问要使得抽水机撑到第 m m m分钟不断水(即剩余水量恒 ≥ 0 \ge0 0),最少的花费是多少;如果撑不到,输出 − 1 -1 1

0 ≤ n ≤ 5 × 1 0 5 , 2 ≤ m ≤ 1 0 9 , 1 ≤ c 0 ≤ c ≤ 1 0 9 0\le n\le5\times10^5,2\le m\le10^9,1\le c0\le c\le10^9 0n5×105,2m109,1c0c109


题解

考虑贪心

先把所有人按照按照时间排序

对于第 i i i个人, Δ t i = t i − t i − 1 \Delta t_i=t_i-t_{i-1} Δti=titi1,我们可以把机器里剩余的水按价格从低到高地用掉,并记录答案

对于每一个人我们把他带的水全部倒进去,直到满了为止,但是先不计费

如果机器里最贵的水价格 > b i \gt b_i >bi,那么我们就可以把最贵的水倒出去一些,再把第 i i i个人的水再倒进去一些(如果还有的话),就相当于那个人来的时候少带了一些水,而且这样也不会影响机器撑到 t i t_i ti

可以发现这样的花费一定是最小的

具体实现可以设 m a p < i n t , i n t > q map<int,int>q map<int,int>q,其中 q [ a ] q[a] q[a]表示机器中剩余的价格为 a a a的水有多少升

用水的时候每次取出最便宜的水(即 q . b e g i n ( ) . s e c o n d q.begin().second q.begin().second)来更新答案,用完后就删除

加水的时候取出最贵的水(即 q . r b e g i n ( ) . f i r s t q.rbegin().first q.rbegin().first)和当前的 b i b_i bi比较,并计算要替换掉多少,完全替换了就删除

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
	char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
	while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
const int N=5e5+5,M=N<<1;
typedef int arr[N];
typedef long long ll;
struct Water{
	int t,a,b;
	inline void in(){sd(t),sd(a),sd(b);}
	bool operator<(const Water&y)const{return t==y.t?(a==y.a?b<y.b:a<y.a):t<y.t;}
}a[N];
int n,m,c,res;map<int,int>q;
inline ll sol(){
	ll ans=0;
	sd(n),sd(m),sd(c),sd(res);
	fp(i,1,n)a[i].in();
	a[++n]=(Water){m,0,0};
	sort(a+1,a+n+1);
	q.clear();q[0]=res;
	fp(i,1,n){
		int DeltaT=a[i].t-a[i-1].t;
		if(DeltaT>c)return -1;
		while(!q.empty()&&DeltaT>0){
			int W=min(DeltaT,q.begin()->second);
			ans+=(ll)q.begin()->first*W;
			q.begin()->second-=W;DeltaT-=W;res-=W;
			if(!q.begin()->second)q.erase(q.begin());
		}
		if(DeltaT)return -1;
		int Cnt=a[i].a,Cost=a[i].b,add=min(c-res,Cnt);
		res+=add;
		while(!q.empty()&&add<Cnt&&Cost<q.rbegin()->first)
			if(Cnt-add>=q.rbegin()->second){
				add+=q.rbegin()->second;
				q.erase(--q.end());
			}else{
				q.rbegin()->second-=Cnt-add;
				add=Cnt;
			}
		q[Cost]+=add;
	}
	return ans;

}
int main(){
	#ifndef ONLINE_JUDGE
		file("s");
	#endif
	int q;sd(q);
	while(q--)printf("%lld\n",sol());
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值