• BZOJ4946 NOI2017蔬菜(贪心+堆)


      容易想到一个费用流做法:将每种蔬菜拆成p种,对应p个过期时间,每一种向可以卖的时间连边,第一次卖的奖励算在最晚过期的一种里。对于天数动态加点。不过这样边数太多了,因为第i天能卖的第i-1天一定能卖,可以改成每一种只向过期时间连边然后第i天向第i-1天连边。这样就有60分了。但费用流没有什么优化空间了。

      如果蔬菜不会过期的话,贪心做法非常显然。那么,考虑让时光倒流。这样蔬菜只会每天增加,每次贪心的选出价值最大的行了。对于奖励,拆成两种蔬菜就好。

      考虑对于单次询问具体应该怎么做。用一个大根堆维护应该选哪些蔬菜。堆里蔬菜分为两类,一类是每天都能拿的,一类只能拿一次的。每次从堆顶拿出m个蔬菜,如果是每天都能拿的再恢复回去,注意考虑各种情况。

      然后考虑多次询问。我们需要从前p天的答案倒推出前p-1天的答案。注意到第p天能选择的第p-1天一定能选择。并且前p-1天的最优选择应该包含在前p天的最优选择内,否则第p天的选择本来就更少没有理由丢掉之前的较优选择。那么从答案里去掉最小的几个使得剩下的不超过(p-1)m个就可以了。

      感觉写的姿势并不对,于是变的异常难写和丑陋不堪。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<stack>
    using namespace std;
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    #define N 100010
    #define inf 1000000000
    #define ll long long
    int n,m,k,cnt=0;
    ll tot=0,ans[N];
    struct data{int v,c,x;
    }a[N<<1];
    struct data2{int i,x;
    }Q[N]; 
    struct data3
    {
        int tim,cnt,val;
        bool operator <(const data3&a) const
        {
            return val<a.val;
        }
    }choose[N*11];
    vector<data3> app[N],rec[N];
    priority_queue<data3> q;
    stack<data3> undo;
    bool cmp(const data2&a,const data2&b)
    {
        return a.x>b.x;
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("bzoj4946.in","r",stdin);
        freopen("bzoj4946.out","w",stdout);
        const char LL[]="%I64d
    ";
    #else
        const char LL[]="%lld
    ";
    #endif
        n=read(),m=read(),k=read();
        for (int i=1;i<=n;i++)
        a[i].v=read(),a[i+n].v=a[i].v+read(),a[i].c=read()-1,a[i+n].c=1,a[i].x=read(),a[i+n].x=0;
        n<<=1;
        for (int i=1;i<=k;i++) Q[i].i=i,Q[i].x=read();
        sort(Q+1,Q+k+1,cmp);
        for (int i=1;i<=n;i++)
        if (a[i].x==0) 
        {
            if (i<=(n>>1)) app[Q[1].x].push_back((data3){0,a[i].c,a[i].v});
            else app[a[i-(n>>1)].x?min(Q[1].x,a[i-(n>>1)].c/a[i-(n>>1)].x+1):Q[1].x].push_back((data3){0,a[i].c,a[i].v});
        }
        else
        {
            if (a[i].c-1ll*a[i].x*Q[1].x>0) app[Q[1].x].push_back((data3){0,a[i].c-1ll*a[i].x*Q[1].x,a[i].v});
            else if (a[i].c%a[i].x) app[a[i].c/a[i].x+1].push_back((data3){0,a[i].c%a[i].x,a[i].v});
            rec[min(a[i].c/a[i].x,Q[1].x)].push_back((data3){min(a[i].c/a[i].x,Q[1].x)+1,a[i].x,a[i].v});
        }
        for (int i=Q[1].x;i;i--)
        {
            for (int j=0;j<app[i].size();j++) q.push(app[i][j]);
            for (int j=0;j<rec[i].size();j++) q.push(rec[i][j]);
            int t=m;
            while (t&&!q.empty())
            {
                if (q.top().tim==0) 
                {
                    if (t>=q.top().cnt) ans[Q[1].i]+=1ll*q.top().cnt*q.top().val,t-=q.top().cnt,choose[++cnt]=q.top();
                    else {ans[Q[1].i]+=1ll*t*q.top().val;undo.push((data3){0,q.top().cnt-t,q.top().val});choose[++cnt]=(data3){0,t,q.top().val};t=0;}
                }
                else
                {
                    int tmp=q.top().cnt*(q.top().tim-i);
                    if (t>=tmp) 
                    {
                        ans[Q[1].i]+=1ll*tmp*q.top().val;
                        choose[++cnt]=(data3){0,tmp,q.top().val};
                        undo.push((data3){i,q.top().cnt,q.top().val});
                        t-=tmp;
                    }
                    else
                    {
                        ans[Q[1].i]+=1ll*t*q.top().val;
                        if (t>=q.top().cnt) choose[++cnt]=(data3){0,q.top().cnt*(t/q.top().cnt),q.top().val};
                        undo.push((data3){q.top().tim-(t-1)/q.top().cnt-1,q.top().cnt,q.top().val});
                        if (t%q.top().cnt) choose[++cnt]=(data3){0,t%q.top().cnt,q.top().val},undo.push((data3){0,q.top().cnt-t%q.top().cnt,q.top().val});
                        t=0;
                    }
                }
                q.pop();
            }
            while (!undo.empty()) q.push(undo.top()),undo.pop();
        }
        sort(choose+1,choose+cnt+1);
        int tot=0;for (int i=1;i<=cnt;i++) tot+=choose[i].cnt;
        int t=2;long long sum=ans[Q[1].i];cnt=1;
        for (int i=Q[1].x-1;i;i--)
        {
            while (tot>i*m)
            {
                if (tot-i*m>=choose[cnt].cnt) sum-=1ll*choose[cnt].cnt*choose[cnt].val,tot-=choose[cnt++].cnt;
                else sum-=1ll*choose[cnt].val*(tot-i*m),choose[cnt].cnt-=tot-i*m,tot=i*m;
            }
            if (i==Q[t].x) ans[Q[t++].i]=sum;
        }
        for (int i=1;i<=k;i++) printf(LL,ans[i]);
        return 0;
    }
  • 相关阅读:
    常用的算法
    2017前端面试题
    深入了解php opcode缓存原理
    0=='aa'的结果是true
    关于PHP浮点数之 intval((0.1+0.7)*10) 为什么是7
    linux grep命令
    linux awk命令详解
    PHP socket模拟POST请求
    shell编程之sed
    Shell脚本常用判断
  • 原文地址:https://www.cnblogs.com/Gloid/p/9473106.html
Copyright © 2020-2023  润新知