【Codeforces 767E】Change-free

贪心。

题目大意:

你有 $1145141919810$ 张百元大钞和 $m$ 枚一元硬币。

你要去买东西,共 $n$ 天。第 $i$ 天要花 $a_i$ 元钱,并且收银员找你 $1$ 元硬币,会增加 $w_i$ 的怒气值。当然找来的钱就归你了。

求一种付钱方案,使得收银员的总怒气值最小。输出方案。

题解:

这种套路题我思考时间有点长了啊

一个物品,要么付 $a_i\bmod 100$ 枚硬币,要么全付 $100$ 元的钞票,给他一枚硬币再让他找回来显然是劣的。

我们考虑先全部让他找钱,记录 $s_i$ 表示第 $i$ 天结束后你拥有的硬币。

然后我们考虑从第 $1$ 天开始,减少找钱的天数。

记录 $cost$ 表示当前已经花掉的硬币个数,那么第 $i$ 天剩余的硬币个数就是 $s_i-cost$。

如果剩下的钱够让这天不找零,那么我们就让他不找零,并将这天省下来的怒气值放入一个小根堆里。

如果剩下的钱不够让这天不找零,我们考虑让之前的一天变回找零,然后使得今天可以不用找零,并且使答案变优

不难发现,从找零变成不找零,所需花费的硬币个数恰好相差 $100$,所以只需要把之前的 $1$ 人换下来就必然够。

那么显然是找增加的怒气值最小的那个换。取堆顶元素进行比较即可。

注意如果 $a_i\equiv 0\pmod{100}$,则它无论如何不会影响答案,可能需要特判。

时间复杂度 $O(n\log n)$。

Code:

#include<cstdio>
#include<queue>
using namespace std;
typedef long long LL;
const int N=1e5+5;
LL s[N],ans,cost;
int n,c[N],w[N],X[N];
bool vis[N];
struct data{
    int w,id;
    inline bool operator<(const data&rhs)const{return w>rhs.w;}
};
priority_queue<data>q;
int main(){
    scanf("%d%lld",&n,s);
    for(int i=1;i<=n;++i){
        scanf("%d",c+i);
        X[i]=c[i]/100,c[i]%=100;
    }
    for(int i=1;i<=n;++i)scanf("%d",w+i),w[i]*=(100-c[i])%100;
    for(int i=1;i<=n;++i)ans+=w[i],s[i]=s[i-1]+(100-c[i])%100;
    for(int i=1;i<=n;++i)if(c[i]){
        if(s[i]-cost>=100){
            cost+=100,ans-=w[i],vis[i]=1;
            q.push((data){w[i],i});
        }else if(!q.empty()&&q.top().w<w[i]){
            data u=q.top();q.pop();
            vis[u.id]=0,ans+=u.w-w[i],vis[i]=1;
            q.push((data){w[i],i});
        }
    }
    printf("%lld\n",ans);
    for(int i=1;i<=n;++i)if(vis[i]||!c[i])printf("%d %d\n",X[i],c[i]);else printf("%d 0\n",X[i]+1);
    return 0;
}