【2018杭电多校Round8 L】From ICPC to ACM
题意
一共有k个月
第i个月原材料价格为c_i ,制造一台电脑要花费一个原材料和m_i元,最多能生产p_i台电脑,客户需求d_i台,
当月卖出的原材料和电脑不收取存储费用,没用完的原材料和电脑可以留到下个月
第i个月能存放e_i台电脑价格为E_i,存放原材料容量无限价格为R_i
求满足每个月客户需求的最小成本
分析
- 因为原材料存储容量无限,如果这个月的c_i加上R_i+1小于c_i+1,那么c_i+1 = c_i+R_i,以此可以得到每个月最便宜的原材料价格。
进而,我们可以换成原材料不要钱,制造一台电脑的费用是c_i+m_i。
进而,我们可以考虑,尽可能多的制造电脑(依据产能),然后卖出客户需求数量,再扔掉超出下个月仓库限制的数量,制造电脑不要钱,只有卖出的时候才算钱,如果扔掉就相当于没有制造过。
所有制造出来的电脑每个月的成本都会加上一个E_i,所以考虑优先卖掉最便宜的电脑,扔掉最贵的电脑。 - 解决插入元素,删除最小的若干元素,最大的若干元素
使用multiset,元素为一个二元组<价格,数量> ,每次删除头尾的若干元素就好
到第i个月,当月存储成本是E_i,有些已经在仓库里的元素其实还加上了∑(k=begin→i-1)E_k,用前缀和思想,我们可以维护一个值add,表示第2个月到当前的所有的存储费用总和,那么插入元素的时候成本可以减去这个add,以后所有元素求费用的时候都加上add
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+5;
const int inf = 0x3fffffff;
multiset< pair<long long,int> >st;
int c[N],d[N],m[N],p[N],e[N],R[N],E[N],n;
long long ans,add,tot;
int main(){
int _;scanf("%d",&_);
while(_--){
st.clear();ans=0LL,add=0,tot=0;;
scanf("%d",&n);
c[0] = inf;
for(int i=1;i<=n;i++) scanf("%d%d%d%d",&c[i],&d[i],&m[i],&p[i]);
for(int i=2;i<=n;i++) scanf("%d%d%d",&e[i],&R[i],&E[i]);
e[n+1] = 0;
int flag = 0;
for(int i=1;i<=n;i++){
c[i] = c[i]<c[i-1]+R[i]?c[i]:c[i-1]+R[i];
add+=E[i];
st.insert(make_pair(c[i]+m[i]-add,p[i]));tot+=p[i];
if(tot<d[i]){ flag = 1;break;}
tot-=d[i];
auto it = st.begin();
while(d[i]>(it->second)){
d[i]-=(it->second);
ans+=it->second*1LL*(add+(it->first));
it = st.erase(it);
}
if(d[i]){
int x = it->first,y = it->second-d[i];
ans+=d[i]*1LL*(add+x);
st.erase(it);
st.insert(make_pair(x,y));
}
if(tot>e[i+1]){
int dec = tot-e[i+1];
tot=e[i+1];
auto it = st.end();it--;
while(dec>it->second){
dec-=it->second;
it=st.erase(it);it--;
}
if(dec){
int x = it->first,y = it->second-dec;
st.erase(it);
st.insert(make_pair(x,y));
}
}
}
if(flag == 1){puts("-1");}
else {printf("%lld\n",ans);}
}
}