[NOI2017]蔬菜 贪心
题解:
首先每天蔬菜会变质这点并不好处理,我们考虑让时间倒流,从后向前处理,这样的话就相当于每天都会得到一定量的蔬菜。
这样做有什么好处呢?
我们可以发现一个性质:如果从后向前贪心卖菜,那么因为现在可以卖的菜,以后一定还可以卖(因为变成了得到菜),因此贪心就是对的了。
因此我们用堆维护一下,从后向前贪心的卖菜,每次优先卖价格高的,第一次卖的菜价格要加上奖励的贡献,并且只能先卖一个,因为卖完这一个之后的同种菜没有奖励了,相当于贡献有变化。
这样向前一直贪到第一天,于是我们就得到了卖1 ~ 100000天的最高收入。
那么已知1到100000的最高收入,如何利用之前的决策信息快速的得知1到99999的最高收入呢?
我们发现,这2者之间唯一的差别就是少买了最多m个蔬菜。
那么我们已知我们已经卖了have个蔬菜,已知1到now这么多天最多卖$m * now$个蔬菜,那么如果$have > m * now$,我们就要舍弃部分蔬菜使得$have <= m * now$成立。
用堆维护,每次撤销卖价最低的蔬菜即可,注意如果撤销的这个蔬菜是剩下的最后一个了,那么代价要加上奖励的代价。
为什么这样是对的呢?
因为如果这个菜是在后面被卖出的话,前面肯定也可以卖,所以如果我们撤销了某天卖出的蔬菜,可以看做在这天后面卖出的蔬菜被挤上来顶替了这个被撤销的蔬菜,于是代价就会始终等价于撤销了最后一天卖出的蔬菜。
- #include<bits/stdc++.h>
- using namespace std;
- #define R register int
- #define AC 100100
- #define LL long long
- int n, m, k, can;//存下浪费了多少的卖菜名额
- const int maxn = ;
- LL ans[AC], last[AC], sum[AC], d[AC], have[AC], v[AC], s[AC], c[AC];
- int sta[AC], top;
- //每一天的答案,每种菜最后一天出现是在哪一天,那天有多少这种菜,d表示以后每天增加多少
- //have表示这种菜现在卖出了多少,a表示卖菜的基础收益,s表示额外收益,c表示初始库存
- int Head[AC], Next[AC], date[AC], tot;
- struct node{
- LL v;
- int id;
- };
- struct cmp2{//小根堆
- bool operator () (node a, node b){ return a.v > b.v;}
- };
- struct cmp{//大根堆
- bool operator () (node a, node b){return a.v < b.v;}
- };
- priority_queue<node, vector<node>, cmp> q;
- priority_queue<node, vector<node>, cmp2> q2;
- inline int read(){
- int x = ;char c = getchar();
- while(c > '' || c < '') c = getchar();
- while(c >= '' && c <= '') x = x * + c - '', c = getchar();
- return x;
- }
- inline void add(int f, int w){
- date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;
- }
- void pre()
- {
- n = read(), m = read(), k = read();
- for(R i = ; i <= n; i ++)
- {
- v[i] = read(), s[i] = read(), c[i] = read(), d[i] = read();
- if(d[i])
- {
- last[i] = c[i] / d[i] + , sum[i] = c[i] - (c[i] / d[i]) * d[i];
- if(!sum[i]) sum[i] = d[i], last[i] --;
- add(last[i], i);//把这个菜挂在第一次出现的地方
- }
- else last[i] = maxn, sum[i] = c[i], add(maxn, i);
- }
- }
- void get()//从后向前贪心,小根堆维护撤销
- {
- for(R i = ; i <= n; i ++)//把卖出去的菜压入栈中
- if(have[i] == ) q2.push((node){v[i] + s[i], i});
- else if(have[i]) q2.push((node){v[i], i});
- for(R i = maxn - ; i; i --)//向前撤销
- {
- ans[i] = ans[i + ];//初始状态
- LL y = m;
- if(i * m >= can) continue;
- else y = can - i * m, can -= y;
- while(y && !q2.empty())
- {
- node x = q2.top();
- if(have[x.id] == ) ans[i] -= x.v, q2.pop(), -- y;//如果只剩一个了就要弹出去了
- else
- {
- if(have[x.id] > y)//如果反正取不完的话就随便取,否则就要注意不能取完了
- {
- have[x.id] -= y, ans[i] -= x.v * y, y = ;
- if(have[x.id] == )
- q2.pop(), x.v += s[x.id], q2.push(x);
- }
- else
- {
- ans[i] -= x.v * (have[x.id] - ), y -= (have[x.id] - );
- have[x.id] = , q2.pop(), x.v += s[x.id], q2.push(x);
- }
- }
- }
- }
- }
- void build()//从后向前贪心,大根堆维护取值
- {
- for(R i = maxn; i; i --)
- {
- for(R j = Head[i]; j; j = Next[j])
- {
- int x = date[j];
- q.push((node){v[x] + s[x], x});
- }
- while(top) q.push((node){v[sta[top]], sta[top]}), -- top;//放回去
- LL y = m;
- while(y && !q.empty())
- {
- node x = q.top();
- if(!have[x.id])
- {
- q.pop(), ans[maxn] += x.v, x.v -= s[x.id];
- have[x.id] = , -- y, q.push(x);
- }
- else
- {
- LL tmp = (last[x.id] - i) * d[x.id] + sum[x.id] - have[x.id];//获取这个菜还剩多少
- if(tmp >= y) have[x.id] += y, ans[maxn] += x.v * y, y = ;//还有就不用pop了
- else
- {
- y -= tmp, have[x.id] += tmp, q.pop();
- ans[maxn] += x.v * tmp;
- if(d[x.id]) sta[++top] = x.id;
- }
- }
- }
- can += m - y;
- }
- }
- void work()
- {
- for(R i = ; i <= k; i ++)
- printf("%lld\n", ans[read()]);
- }
- int main()
- {
- // freopen("in.in", "r", stdin);
- pre();
- build();//先获取最后一个的值
- get();//再倒退出整个数组
- work();
- // fclose(stdin);
- return ;
- }
[NOI2017]蔬菜 贪心的更多相关文章
- NOI2017蔬菜(贪心)
小 N 是蔬菜仓库的管理员,负责设计蔬菜的销售方案. 在蔬菜仓库中,共存放有 n 种蔬菜,小 N 需要根据不同蔬菜的特性,综合考虑各 方面因素,设计合理的销售方案,以获得最多的收益. 在计算销售蔬菜的 ...
- BZOJ.4946.[NOI2017]蔬菜(贪心 离线)
题目链接 因为有删除,考虑倒序处理某个p的询问. 那么每天删除xi的蔬菜就变成了每天运来xi的蔬菜.那么我们取当前最优的即可,早取晚取都一样,不需要留给后面取,还能给后面更优的留出空间. 这样就只需考 ...
- 【BZOJ4946】[NOI2017]蔬菜(贪心)
[BZOJ4946][NOI2017]蔬菜(贪心) 题面 BZOJ 洛谷 UOJ 题解 忽然发现今年\(NOI\)之前的时候切往年\(NOI\)的题目,就\(2017\)年的根本不知道怎么下手(一定是 ...
- bzoj4946: [Noi2017]蔬菜 神烦贪心
题目链接 bzoj4946: [Noi2017]蔬菜 题解 挺神的贪心 把第次买的蔬菜拆出来,记下每种蔬菜到期的日期,填第一单位蔬菜比其他的要晚 按价格排序后,贪心的往前面可以填的位置填就可以了.找可 ...
- [NOI2017]蔬菜
[NOI2017]蔬菜 题目描述 大意就是有\(n\)种物品,第\(i\)个物品有\(c_i\)个,单价是\(a_i\).然后每天你可以卖出最多\(m\)个物品.每天结束后第\(i\)种物品会减少\( ...
- BZOJ4946[Noi2017]蔬菜——线段树+堆+模拟费用流
题目链接: [Noi2017]蔬菜 题目大意:有$n$种蔬菜,每种蔬菜有$c_{i}$个,每种蔬菜每天有$x_{i}$个单位会坏掉(准确来说每天每种蔬菜坏掉的量是$x_{i}-$当天这种蔬菜卖出量), ...
- 4946: [Noi2017]蔬菜
4946: [Noi2017]蔬菜 http://www.lydsy.com/JudgeOnline/upload/Noi2017D2.pdf 分析: 贪心. 首先可以将一个蔬菜拆成两个,一个是有加成 ...
- BZOJ4946 NOI2017蔬菜(贪心+堆)
容易想到一个费用流做法:将每种蔬菜拆成p种,对应p个过期时间,每一种向可以卖的时间连边,第一次卖的奖励算在最晚过期的一种里.对于天数动态加点.不过这样边数太多了,因为第i天能卖的第i-1天一定能卖,可 ...
- uoj318 [NOI2017]蔬菜 【贪心 + 堆 + 并查集】
题目链接 uoj 题解 以前看别人博客,在考场上用费用流做,一直以为这题是毒瘤网络流题 没想到竟然是贪心模拟题... 如果只有一个蔬菜呢?这就是一个经典的普及难度的贪心,正着推面临优先选择的困难,而逆 ...
随机推荐
- Awesome Django
Awesome Django If you find Awesome Django useful, please consider donating to help maintain it. ...
- 学会了vim中的自动补全功能
好开心,再也不用再多个工具之间切换了,哈哈 擦,功能太弱
- 负数取余/整除,Python和C语言的不同
总结一句:Python中负数整除,是向负无穷取整,所以导致负数取余不对 在数学公式中,两种语言的表示算法都是一样的,都是: r=a-n*[a/n] 以上,r是余数,a是被除数,n是除数. 唯一不同点, ...
- TW实习日记:第27天
今天依旧是磨洋工的一天,说真的,被存在各种问题的后端接口把耐心和动力都给磨没了.于是一天就又在沟通接口问题中度过了,完善了一个新功能,将一个新功能开发到了一半.效率可真是够低的,唉.然后不知道为什么突 ...
- JAVA基础学习之路(七)对象数组的定义及使用
两种定义方式: 1.动态初始化: 定义并开辟数组:类名称 对象数组名[] = new 类名称[长度] 分布按成:类名称 对象数组名[] = null: 对象数组名 = new 类名称[长度]: 2 ...
- Pandas基础教程
pandas教程 更多地可以 参考教程 安装 pip install pandas pandas的类excel操作,超级方便: import pandas as pd dates = pd.date_ ...
- Java进阶知识点:不要只会写synchronized - JDK十大并发编程组件总结
一.背景 提到Java中的并发编程,首先想到的便是使用synchronized代码块,保证代码块在并发环境下有序执行,从而避免冲突.如果涉及多线程间通信,可以再在synchronized代码块中使用w ...
- sql server存储特殊字符解决办法
好久没来院子了,最近在学java了,再加上项目比较紧,最近都没怎么上,其实这几天在项目中学到不少东西,都能写下来,但是久而久之就忘了,还是得养成及时总结的好习惯啊,还有有时间一定要把那个小项目整理下来 ...
- ubuntu16.04图形界面安装中文输入法,中文展示
打开system Settings 设置 打开设置语言 安装Language Support 点击installed languages 选择chinese 打勾,安装 安装IBus框 ...
- 聊聊、dubbo 找不到 dubbo.xsd 报错
平常在用 Dubbo 的时候,创建 xml 会提示 http://code.alibabatech.com/schema/dubbo/dubbo.xsd 找不到. 大家可以去 https://gith ...