• [NOI2017]蔬菜 贪心


    题面: [NOI2017]蔬菜

    题解:

      首先每天蔬菜会变质这点并不好处理,我们考虑让时间倒流,从后向前处理,这样的话就相当于每天都会得到一定量的蔬菜。

      这样做有什么好处呢?

      我们可以发现一个性质:如果从后向前贪心卖菜,那么因为现在可以卖的菜,以后一定还可以卖(因为变成了得到菜),因此贪心就是对的了。

      因此我们用堆维护一下,从后向前贪心的卖菜,每次优先卖价格高的,第一次卖的菜价格要加上奖励的贡献,并且只能先卖一个,因为卖完这一个之后的同种菜没有奖励了,相当于贡献有变化。

      这样向前一直贪到第一天,于是我们就得到了卖1 ~ 100000天的最高收入。

      那么已知1到100000的最高收入,如何利用之前的决策信息快速的得知1到99999的最高收入呢?

      我们发现,这2者之间唯一的差别就是少买了最多m个蔬菜。

      那么我们已知我们已经卖了have个蔬菜,已知1到now这么多天最多卖$m * now$个蔬菜,那么如果$have > m * now$,我们就要舍弃部分蔬菜使得$have <= m * now$成立。

      用堆维护,每次撤销卖价最低的蔬菜即可,注意如果撤销的这个蔬菜是剩下的最后一个了,那么代价要加上奖励的代价。

      为什么这样是对的呢?

      因为如果这个菜是在后面被卖出的话,前面肯定也可以卖,所以如果我们撤销了某天卖出的蔬菜,可以看做在这天后面卖出的蔬菜被挤上来顶替了这个被撤销的蔬菜,于是代价就会始终等价于撤销了最后一天卖出的蔬菜。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 100100
      5 #define LL long long
      6 
      7 int n, m, k, can;//存下浪费了多少的卖菜名额
      8 const int maxn = 100000;
      9 LL ans[AC], last[AC], sum[AC], d[AC], have[AC], v[AC], s[AC], c[AC];
     10 int sta[AC], top;
     11 //每一天的答案,每种菜最后一天出现是在哪一天,那天有多少这种菜,d表示以后每天增加多少
     12 //have表示这种菜现在卖出了多少,a表示卖菜的基础收益,s表示额外收益,c表示初始库存
     13 int Head[AC], Next[AC], date[AC], tot;
     14 struct node{
     15     LL v;
     16     int id;
     17 };
     18 
     19 struct cmp2{//小根堆
     20     bool operator () (node a, node b){ return a.v > b.v;}
     21 };
     22 
     23 struct cmp{//大根堆
     24     bool operator () (node a, node b){return a.v < b.v;}
     25 };
     26 
     27 priority_queue<node, vector<node>, cmp> q;
     28 priority_queue<node, vector<node>, cmp2> q2;
     29 
     30 inline int read(){
     31     int x = 0;char c = getchar();
     32     while(c > '9' || c < '0') c = getchar();
     33     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     34     return x;
     35 }
     36 
     37 inline void add(int f, int w){
     38     date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;
     39 }
     40 
     41 void pre()
     42 {
     43     n = read(), m = read(), k = read();
     44     for(R i = 1; i <= n; i ++)
     45     {
     46         v[i] = read(), s[i] = read(), c[i] = read(), d[i] = read();
     47         if(d[i])
     48         {
     49             last[i] = c[i] / d[i] + 1, sum[i] = c[i] - (c[i] / d[i]) * d[i];
     50             if(!sum[i]) sum[i] = d[i], last[i] --;
     51             add(last[i], i);//把这个菜挂在第一次出现的地方
     52         }
     53         else last[i] = maxn, sum[i] = c[i], add(maxn, i);
     54     }
     55 }
     56 
     57 void get()//从后向前贪心,小根堆维护撤销
     58 {
     59     for(R i = 1; i <= n; i ++)//把卖出去的菜压入栈中
     60         if(have[i] == 1) q2.push((node){v[i] + s[i], i});
     61         else if(have[i]) q2.push((node){v[i], i});
     62     for(R i = maxn - 1; i; i --)//向前撤销
     63     {
     64         ans[i] = ans[i + 1];//初始状态
     65         LL y = m;
     66         if(i * m >= can) continue;
     67         else y = can - i * m, can -= y;
     68         while(y && !q2.empty())
     69         {
     70             node x = q2.top();
     71             if(have[x.id] == 1) ans[i] -= x.v, q2.pop(), -- y;//如果只剩一个了就要弹出去了
     72             else
     73             {
     74                 if(have[x.id] > y)//如果反正取不完的话就随便取,否则就要注意不能取完了
     75                 {
     76                     have[x.id] -= y, ans[i] -= x.v * y, y = 0;
     77                     if(have[x.id] == 1)
     78                         q2.pop(), x.v += s[x.id], q2.push(x);
     79                 }
     80                 else
     81                 {
     82                     ans[i] -= x.v * (have[x.id] - 1), y -= (have[x.id] - 1);
     83                     have[x.id] = 1, q2.pop(), x.v += s[x.id], q2.push(x);
     84                 }
     85             }
     86         }        
     87     }
     88 }
     89 
     90 void build()//从后向前贪心,大根堆维护取值
     91 {
     92     for(R i = maxn; i; i --)
     93     {
     94         for(R j = Head[i]; j; j = Next[j])
     95         {
     96             int x = date[j];
     97             q.push((node){v[x] + s[x], x});
     98         }
     99         while(top) q.push((node){v[sta[top]], sta[top]}), -- top;//放回去
    100         LL y = m;
    101         while(y && !q.empty())
    102         {
    103             node x = q.top();
    104             if(!have[x.id])
    105             {
    106                 q.pop(), ans[maxn] += x.v, x.v -= s[x.id];
    107                 have[x.id] = 1, -- y, q.push(x);
    108             }
    109             else
    110             {
    111                 LL tmp = (last[x.id] - i) * d[x.id] + sum[x.id] - have[x.id];//获取这个菜还剩多少
    112                 if(tmp >= y) have[x.id] += y, ans[maxn] += x.v * y, y = 0;//还有就不用pop了
    113                 else 
    114                 {
    115                     y -= tmp, have[x.id] += tmp, q.pop();
    116                     ans[maxn] += x.v * tmp;
    117                     if(d[x.id]) sta[++top] = x.id; 
    118                 }
    119             }
    120         }
    121         can += m - y;
    122     }
    123 }
    124 
    125 void work()
    126 {
    127     for(R i = 1; i <= k; i ++)
    128         printf("%lld
    ", ans[read()]);
    129 }
    130 
    131 int main()
    132 {
    133 //    freopen("in.in", "r", stdin);
    134     pre();
    135     build();//先获取最后一个的值
    136     get();//再倒退出整个数组
    137     work();
    138 //    fclose(stdin);
    139     return 0;
    140 }
    View Code
  • 相关阅读:
    centos7系统中忘记了root管理员账号密码的解决方式
    【python之路48】生成器表达式、推导式
    小米集团信息化中台战略
    分时函数
    函数节流
    JS浮点计算问题
    要转型做前端开发了
    优秀的开发人员和测试人员应有的态度
    C#数组的笔记
    LINQ不包含列表
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9914964.html
Copyright © 2020-2023  润新知