由于牛奶只喝最新鲜的,符合先进先出的特征,使用栈来维护。

每次在栈中加入新牛奶,然后计算,对于接下来的若干天(由下次向栈中加入牛奶的时间计算得到的天数),目前的栈状态能够满足多少天的牛奶需求。

具体地,计算无视过期限制的情况下栈顶的牛奶够喝多少天,以及栈顶牛奶的过期时间,利用这两个信息计算当前栈顶对答案的贡献之后修改栈顶。

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

#include<bits/stdc++.h>
using namespace std;
constexpr long long inf=1'000'000'000'000'000ll;
struct Milk{long long d,w;}s[100'005];
long long ans;
int n,m,k;
void solution(){
    cin>>n>>m>>k,ans=0ll;
    for(int i=1;i<=n;i++) cin>>s[i].d>>s[i].w;
    s[n+1].d=inf,s[n+1].w=0ll;
    vector<Milk> v;
    for(int i=1;i<=n;i++){
        long long resday=s[i+1].d-s[i].d;//下一次加奶前还剩多少天
        long long nxtned=0ll;//需要的零头
        v.push_back(s[i]);
        while(!v.empty()&&resday!=0ll){
            long long lim=v.back().d+k-1;//保质期
            if(lim<s[i+1].d-resday){//过期
                v.clear();
                continue;
            }
            if(nxtned!=0ll){//处理零头
                long long red=min(nxtned,v.back().w);
                nxtned-=red,v.back().w-=red;
                if(nxtned==0ll) --resday,++ans;//零头用完了
                if(v.back().w==0ll){//牛奶喝完力
                    v.pop_back();
                    continue;
                }
            }
            long long drink=v.back().w/m;//牛奶的量还能喝几天
            long long good=lim-(s[i+1].d-resday)+1;//牛奶还有几天过期
            long long add=min(min(drink,good),resday);//真能喝几天
            ans+=add,resday-=add,v.back().w-=add*m;
            if(resday!=0ll&&add<good&&v.back().w!=0ll){//没过期还有零头
                nxtned=m-v.back().w;
                v.pop_back();
            }
            if(v.back().w==0ll) v.pop_back();
        }
    }
    cout<<ans<<'\n';
}
int T;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>T;
    while(T--) solution();
    return 0;
}
最后修改:2024 年 11 月 11 日
如果觉得我的文章对你有用,请随意赞赏