题解:P4181 [USACO18JAN] Rental Service S
我们发现,出租一头奶牛产生的利润与奶牛的产奶量 \(c\) 无关,所以我们可以考虑把产奶量低的奶牛拿出去出租,剩下的就去产奶卖钱。卖奶和出租始终考虑价高者得原则,即按照收购价格从大到小排序,依次分配给每一个订单。
具体地,我们将产奶量 \(c_i\) 从大到小排序,然后枚举一个 \(k\),让编号为 \(1 \sim k\) 的奶牛产奶,\(k+1 \sim n\) 的奶牛去出租,然后计算一下该方案的利润,最后将所有方案取一个利润最大值就是最终的答案。
【出租】:对于第 \(k+1 \sim n\) 头奶牛,可以按照出租的价格 \(r_i\) 从大到小依次分配给每个出租订单,这一部分可以用前缀和 \(O(1)\) 计算;
【卖奶】:对于第 \(1 \sim k\) 头奶牛,计算出这一部分奶牛产奶量的总和,然后用两个数组 \(sp_i,sq_i\) 表示卖 \(sp_i\) 单位的牛奶可以赚 \(sq_i\) 的钱,这两个也可以用前缀和的思想 \(O(n)\) 预处理。
查询卖 \(k\) 的牛奶能赚多少钱的话可以参考初学 OI 时的分段收费问题,把 \(k\) 分成若干段,每一段对应一个订单的 \(p,q\),在 \(sq_i\) 里面二分查找一个第一个 \(\ge k\) 的 \(sq_i\),然后就可以分开两半计算:一半是完整的若干段 \(sp_{i-1}\),另一半是多出来的 \(k-sq_{i-1}\) 单位的牛奶,这一部分的单价是 \(p_i\)。注意考虑刚好卖完一段和 \(i=0\) 的情况。
参考程序(注:优先队列可换成结构体排序的形式,因为之前写了个假的做法就是用的优先队列):
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=1e5+7;
int n,m,r;
int c[N],b[N];
ll sc[N]; //c的前缀和
struct request{int p,q;};
struct cmp{bool operator()(request A,request B){return A.p<B.p;}};
priority_queue<request,vector<request>,cmp> sell;
int p[N],q[N],rent[N];
ll sp[N],sq[N]; //卖sq[i]的牛奶能赚sp[i]的钱
ll sr[N]; //rent的前缀和(绝对不是star rail)
inline ll calc(ll k) //卖k的牛奶能赚多少钱
{
int i=lower_bound(sq+1,sq+m+1,k)-sq;
// cerr<<sp[i]<<' '<<sq[i]<<' '<<p[i]<<','<<i<<' '<<k<<endl;
if(i==0) return k*p[1];
if(sq[i]==k) return sp[i];
else return sp[i-1]+(k-sq[i-1])*p[i];
}
int main()
{
// freopen("neuvillette.in","r",stdin);
// freopen("neuvillette.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>m>>r;
for(int i=1;i<=n;i++) cin>>c[i];
sort(c+1,c+n+1,greater<int>());
for(int i=1;i<=n;i++) sc[i]=sc[i-1]+c[i];
for(int i=1,p,q;i<=m;i++)
{
cin>>q>>p;
sell.push({p,q});
}
for(int i=1;i<=r;i++) cin>>rent[i];
sort(rent+1,rent+r+1,greater<int>());
for(int i=1;i<=n;i++) sr[i]=sr[i-1]+rent[i];
for(int i=1;i<=m;i++)
{
p[i]=sell.top().p;
q[i]=sell.top().q;
sp[i]=sp[i-1]+sell.top().p*q[i];
sq[i]=sq[i-1]+sell.top().q;
sell.pop();
}
ll ans=-12180211;
for(int k=1;k<=n;k++)
{
ans=max(ans,calc(sc[k])+sr[n-k]);
// cerr<<k<<' '<<sc[k]<<' '<<calc(sc[k])<<' '<<sr[n-k]<<endl;
}
cout<<ans;
cout.flush();
return 0;
}
/*
把奶牛的产奶量从大到小排序完之后,考虑派出[1,k]的牛产奶,[k+1,n]的牛出租
然后计算价格,找出一个最优的方案出来
出租的牛一共有(n-(k+1)+1)=n-k头
以下做法是假的,以下做法是假的,以下做法是假的
for i=1~n考虑当前牛是卖奶还是出租
如果出租带来的收益比卖奶要大那就卖奶,反之亦然
处理牛奶委托和出租委托的策略是按照价格从大到小排序
*/

浙公网安备 33010602011771号