题意

一共有k个月
第i个月原材料价格为c_i ,制造一台电脑要花费一个原材料和m_i元,最多能生产p_i台电脑,客户需求d_i台,
当月卖出的原材料和电脑不收取存储费用,没用完的原材料和电脑可以留到下个月
第i个月能存放e_i台电脑价格为E_i,存放原材料容量无限价格为R_i
求满足每个月客户需求的最小成本

分析

  1. 因为原材料存储容量无限,如果这个月的c_i加上R_i+1小于c_i+1,那么c_i+1 = c_i+R_i,以此可以得到每个月最便宜的原材料价格。
    进而,我们可以换成原材料不要钱,制造一台电脑的费用是c_i+m_i。
    进而,我们可以考虑,尽可能多的制造电脑(依据产能),然后卖出客户需求数量,再扔掉超出下个月仓库限制的数量,制造电脑不要钱,只有卖出的时候才算钱,如果扔掉就相当于没有制造过。
    所有制造出来的电脑每个月的成本都会加上一个E_i,所以考虑优先卖掉最便宜的电脑,扔掉最贵的电脑。
  2. 解决插入元素,删除最小的若干元素,最大的若干元素
    使用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);}
    }
}