题解:P10001 [集训队互测 2023] 优惠购物
george0929 · · 题解
QOJ 最短解第一页。
最大化使用的券个数,策略形如攒券然后集体花掉。
笔者一开始的想法是通过调整法证明,用券个数在
因为是每
从前往后买时,是否节省券是未知的,因此考虑反悔贪心,维护每个前缀的最优解,并动态进行调整。
假设我们已经确定了
令
使用堆优化此过程,存储
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll inf=1e18;
int n;
ll m,c,a[1000005],b[1000005],y[1000005],d[1000005];
priority_queue<pair<ll,int>> Q;
void Inq(int i){
d[i]=c-(y[i]%c);
if(y[i]+d[i]>a[i]) d[i]=inf;
if(y[i]<a[i]) Q.push({-d[i],i});
}
void solve(){
cin>>n>>m>>c;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
ll T=m;
for(int i=1;i<=n;i++){
y[i]=a[i]-b[i];
while(!Q.empty()&&a[i]-y[i]-T>=-Q.top().first){
int j=Q.top().second;Q.pop();
if(d[j]==c){
ll k=max(min((a[i]-y[i]-T)/(c+1),(a[j]-y[j])/c),1ll);
y[j]+=c*k,T+=(c+1)*k;
}else y[j]+=d[j],T+=d[j]+1;
Inq(j);
}
Inq(i);
while(!Q.empty()&&T<a[i]-y[i]){
int j=Q.top().second;Q.pop();
if(j==i){
if(d[i]>=c) y[i]=a[i]-T;
else y[i]=min(y[i]+d[i],a[i]-T);
Inq(i);
}else{
ll cnt=a[i]-y[i]-T;
y[j]+=cnt,T+=cnt;
Inq(j);
}
}
T-=(a[i]-y[i]);
T+=(y[i]/c);
}
while(!Q.empty()) Q.pop();
ll res=0;
for(int i=1;i<=n;i++) res+=y[i];
cout<<res<<"\n";
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--) solve();
return 0;
}