洛谷P3826/LOJ2306/UOJ318/BZOJ4946[NOI2017]蔬菜

首先想一想只有一组询问该怎么做。

菜烂掉比较不好处理,考虑反着来,每天都有菜变多,那么就可以贪心地每天选择收益最高的$m$份菜卖掉。具体实现的话,记录每样菜在什么时候烂光,在相应的一天以$a_i+s_i$的权值插进大根堆里,每次取出堆顶时判断一下,如果当前菜是第一次卖则只卖一个,然后把它以$a_i$的权值插回去;否则尽可能卖光,如果今天卖光了的话先临时存起来,今天结束了再插回去,防止堆顶始终是卖完的菜,引发死循环。

假设现在我们已经求出了$100000$天的答案,如何求出$99999$天的呢?我们发现菜烂掉有一个良好的性质:今天能卖的菜昨天一定也能卖。这样的话,我们就可以考虑把$99999m$份菜在前$99999$天卖掉,那么从$100000$天到$99999$天的损失就只有剩下第$100000$要卖的菜。同样地,我们用小根堆来维护最便宜的菜,把它们放在最后一天损失掉一定是最优的,这样就可以由$100000$天的答案倒退回$1$天的答案。

洛谷P3826/LOJ2306/UOJ318/BZOJ4946[NOI2017]蔬菜
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
const int N=100050;
char rB[1<<21],*rS,*rT,wB[1<<21];
int wp=-1;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
inline void pc(char c){if(wp+1==(1<<21))flush();wB[++wp]=c;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short buf[25];
inline void wt(ll x){
    short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    pc(x|48);
    while(l>=0)pc(buf[l--]|48);
    pc('\n');
}
int a[N],s[N],c[N],x[N],cnt[N],tmp[N],tmpl=-1;
ll ans[N];
vector<int> p[N];
struct node{
    int p,v;
    node(){}
    node(int p,int v):p(p),v(v){}
    inline bool operator <(const node &b)const{return v<b.v;}
};
priority_queue<node> Q;
struct nod{
    int p,v;
    nod(){}
    nod(int p,int v):p(p),v(v){}
    inline bool operator <(const nod &b)const{return v>b.v;}
};
priority_queue<nod> q;
inline int Min(int a,int b){return a<b?a:b;}
inline bool cmp(int x,int y){return p[x]<p[y];}
int main(){
    int n=rd(),m=rd(),k=rd(),i,j,lim,cur,t,tot=0;
    for(i=1;i<=n;++i){a[i]=rd();s[i]=rd();c[i]=rd();x[i]=rd();}
    for(i=1;i<=n;++i)if(x[i])p[Min(100000,(c[i]+x[i]-1)/x[i])].push_back(i);
    else p[100000].push_back(i);
    for(i=100000;i;--i){
        for(j=0;j<p[i].size();++j)Q.push(node(p[i][j],a[p[i][j]]+s[p[i][j]]));
        if(Q.empty())continue;
        lim=m;
        while(lim&&!Q.empty()){
            cur=Q.top().p;Q.pop();
            if(!cnt[cur]){
                cnt[cur]=1;ans[100000]+=a[cur]+s[cur];++tot;--lim;
                if(c[cur]>1)Q.push(node(cur,a[cur]));
            }else{
                t=Min(lim,c[cur]-(i-1)*x[cur]-cnt[cur]);
                cnt[cur]+=t;ans[100000]+=(ll)a[cur]*t;tot+=t;lim-=t;
                if(c[cur]>cnt[cur])tmp[++tmpl]=cur;
            }
        }
        while(tmpl>=0){Q.push(node(tmp[tmpl],a[tmp[tmpl]]));--tmpl;}
    }
    for(i=1;i<=n;++i)if(cnt[i])if(cnt[i]==1)q.push(nod(i,a[i]+s[i]));
    else q.push(nod(i,a[i]));
    for(i=99999;i;--i){
        ans[i]=ans[i+1];
        if(tot<=m*i)continue;
        lim=tot-m*i;
        while(lim){
            cur=q.top().p;q.pop();
            if(cnt[cur]==1){cnt[cur]=0;ans[i]-=a[cur]+s[cur];--lim;}
            else{
                t=Min(lim,cnt[cur]-1);
                ans[i]-=(ll)a[cur]*t;lim-=t;
                if((cnt[cur]-=t)==1)q.push(nod(cur,a[cur]+s[cur]));
                else q.push(nod(cur,a[cur]));
            }
        }
        tot=m*i;
    }
    while(k--)wt(ans[rd()]);
    flush();
    return 0;
}
View Code

 

上一篇:day05


下一篇:Apache Hive Random