传送门:CF767E
题解
一道不错的贪心:
显然每次只用零钱付
c
i
%
100
c_i\%100
ci%100的部分。
若
w
≥
c
i
%
100
w\geq c_i\%100
w≥ci%100,则可以直接支付没有找零;否则只能给多给一张100,怒气值+
w
i
(
100
−
c
i
%
100
)
w_i(100-c_i\%100)
wi(100−ci%100),且获得了
100
−
c
i
%
100
100-c_i\%100
100−ci%100个硬币。
于是发现总的硬币消耗个数是一定的,最少的需要找零的次数也是一定的。
可以把给100的操作看做花费
w
i
(
100
−
c
i
%
100
)
w_i(100-c_i\%100)
wi(100−ci%100)的代价新获得了100个硬币,同时这一轮给出了
c
i
%
100
c_i\%100
ci%100个硬币。
把给钱和获得钱的操作分开考虑,那么只需要保证第 i i i次操作时硬币数量足够即可,当硬币数量不够支付零钱时,再从前 i i i天所有没有换过前的天里面找代价最小的一天换钱即可。用堆维护最小值。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int n,m,c[N],w[N];
bool cg[N];
ll ans,nw;
struct P{
int id;ll v;
bool operator <(const P&ky)const{return v>ky.v;}
}tp;
priority_queue<P>que;
int main(){
int i,j;
scanf("%d%I64d",&n,&nw);
for(i=1;i<=n;++i) scanf("%d",&c[i]);
for(i=1;i<=n;++i) scanf("%d",&w[i]);
for(i=1;i<=n;++i) if((j=c[i]%100)){
que.push({i,(ll)w[i]*(100-j)});
nw-=j;if(nw<0){
tp=que.top();que.pop();cg[tp.id]=true;
ans+=tp.v;nw+=100;
}
}
printf("%I64d\n",ans);
for(i=1;i<=n;++i){
if(cg[i]) printf("%d %d\n",c[i]/100+1,0);
else printf("%d %d\n",c[i]/100,c[i]%100);
}
return 0;
}