【BZOJ4946】[NOI2017]蔬菜(贪心)

题面

BZOJ

洛谷

UOJ

题解

忽然发现今年\(NOI\)之前的时候切往年\(NOI\)的题目,就\(2017\)年的根本不知道怎么下手(一定是我太菜了)

这题是一道神仙题(下定义),然而部分分多得不得了,不知道写一个费用流可以得多少分。

我决定先强行插入一下费用流的做法,费用流是这样子的:首先对于蔬菜拆点,每一天拆出一个点,因为蔬菜可以购买的量逐渐递减,因此每一天向下一天连接流量为当前天减少\(d\)的边,费用为\(0\),然后考虑把蔬菜卖出去,那么就是从一个蔬菜拆出来的某一天向汇点连边,因为限制每天购买的总量,所以再对于每一天的购买的蔬菜拆一个点,然后这一天的每一个蔬菜向这个点连容量为\(inf\),费用为蔬菜费用的边,再从这个点向汇点连容量为\(m\),费用为\(0\)的边。显然源点向每个蔬菜的第一天连容量为蔬菜数量,费用为\(0\)的边,至于第一次购买产生的额外贡献,我们把连的那条边拆出一个单位来,再额外链接一下容量为\(1\),费用为第一次购买产生的额外贡献的边。这样子连边就好了(应该是对的)。

跳出来,往正解的方面想。

显然不难发现一个\(O(nQ)\)的贪心,蔬菜会逐渐减少很不好做,我们倒过来,反过来考虑每一天,那么蔬菜的数量变成了每一天都增加每种蔬菜一定量,然后我们需要倒着买蔬菜就好了。可能需要数据结构什么的维护一下,但是大致的复杂度就是上述的东西。

我们现在再正着考虑,假设我们知道我们在\(p\)天的时候的最优解中,买了哪些蔬菜,那么我们可以很容易的得到\(p-1\)天的答案,显然只需要把利润最小的那\(m\)个蔬菜给去掉就好了,因为第\(p-1\)天可以购买的蔬菜不会少于第\(p\)天,在第\(p\)天能够买到的,在\(p-1\)天也一定能够买到。

前面说的不是很清楚,现在考虑如何求解第\(p\)天的答案。我们既然是增加蔬菜,那么这个操作很容易维护,只需要搞一个堆出来,然后每次把当前所拥有的所有蔬菜全部拿出来贪心取就好了,稍微注意一下第一次选产生的额外贡献的细节就好。然后考虑如何递推回去,还是拿一个堆维护,同理注意一下第一次选产生的额外贡献。

既然这么讲了贪心怎么写,是不是觉得其实这就是一个模拟费用流的过程啊,倒推回去就是一个退流的过程,正推的贪心,显然每天只有那么几条路径,用堆维护等价于跑费用流,忽然感觉很妙啊。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 100100
#define pb push_back
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Node{int v,i;}S[MAX];
bool operator<(Node a,Node b){return a.v<b.v;}
int P=1e5,top,sum;
int n,m,Qry,a[MAX],s[MAX],c[MAX],x[MAX],used[MAX];
ll ans[MAX];
bool vis[MAX];
vector<int> d[MAX];
priority_queue<Node> Q;
int main()
{
n=read();m=read();Qry=read();
for(int i=1;i<=n;++i)a[i]=read(),s[i]=read(),c[i]=read(),x[i]=read();
for(int i=1;i<=n;++i)
if(!x[i])d[P].pb(i);
else d[min(P,(c[i]+x[i]-1)/x[i])].pb(i);
for(int i=P;i;--i)
{
for(int j=0,l=d[i].size();j<l;++j)
Q.push((Node){a[d[i][j]]+s[d[i][j]],d[i][j]});
if(Q.empty())continue;
for(int j=m;j&&!Q.empty();)
{
Node u=Q.top();Q.pop();
if(!vis[u.i])
{
vis[u.i]=true;ans[P]+=u.v;used[u.i]+=1;--j;
if(c[u.i]>1)Q.push((Node){a[u.i],u.i});
}
else
{
int rest=min(j,c[u.i]-used[u.i]-(i-1)*x[u.i]);
ans[P]+=1ll*rest*u.v;used[u.i]+=rest;j-=rest;
if(used[u.i]!=c[u.i])S[++top]=(Node){a[u.i],u.i};
}
}
while(top)Q.push(S[top--]);
}
while(!Q.empty())Q.pop();
for(int i=1;i<=n;++i)sum+=used[i];
for(int i=1;i<=n;++i)
if(used[i]==1)Q.push((Node){-s[i]-a[i],i});
else if(used[i])Q.push((Node){-a[i],i});
for(int i=P-1;i;--i)
{
ans[i]=ans[i+1];
while(sum>i*m&&!Q.empty())
{
Node u=Q.top();Q.pop();u.v*=-1;
if(used[u.i]>1)
{
int rest=min(sum-i*m,used[u.i]-1);
used[u.i]-=rest;sum-=rest;ans[i]-=1ll*rest*u.v;
if(used[u.i]==1)Q.push((Node){-a[u.i]-s[u.i],u.i});
else Q.push((Node){-a[u.i],u.i});
}
else --sum,--used[u.i],ans[i]-=u.v;
}
}
while(Qry--)printf("%lld\n",ans[read()]);
return 0;
}

【BZOJ4946】[NOI2017]蔬菜(贪心)的更多相关文章

  1. bzoj4946: [Noi2017]蔬菜 神烦贪心

    题目链接 bzoj4946: [Noi2017]蔬菜 题解 挺神的贪心 把第次买的蔬菜拆出来,记下每种蔬菜到期的日期,填第一单位蔬菜比其他的要晚 按价格排序后,贪心的往前面可以填的位置填就可以了.找可 ...

  2. BZOJ4946[Noi2017]蔬菜——线段树+堆+模拟费用流

    题目链接: [Noi2017]蔬菜 题目大意:有$n$种蔬菜,每种蔬菜有$c_{i}$个,每种蔬菜每天有$x_{i}$个单位会坏掉(准确来说每天每种蔬菜坏掉的量是$x_{i}-$当天这种蔬菜卖出量), ...

  3. [NOI2017]蔬菜 贪心

    题面: [NOI2017]蔬菜 题解: 首先每天蔬菜会变质这点并不好处理,我们考虑让时间倒流,从后向前处理,这样的话就相当于每天都会得到一定量的蔬菜. 这样做有什么好处呢? 我们可以发现一个性质:如果 ...

  4. BZOJ4946 NOI2017蔬菜(贪心+堆)

    容易想到一个费用流做法:将每种蔬菜拆成p种,对应p个过期时间,每一种向可以卖的时间连边,第一次卖的奖励算在最晚过期的一种里.对于天数动态加点.不过这样边数太多了,因为第i天能卖的第i-1天一定能卖,可 ...

  5. bzoj4946 Noi2017 蔬菜

    题目描述 小 N 是蔬菜仓库的管理员,负责设计蔬菜的销售方案. 在蔬菜仓库中,共存放有nn 种蔬菜,小NN 需要根据不同蔬菜的特性,综合考虑各方面因素,设计合理的销售方案,以获得最多的收益. 在计算销 ...

  6. NOI2017蔬菜(贪心)

    小 N 是蔬菜仓库的管理员,负责设计蔬菜的销售方案. 在蔬菜仓库中,共存放有 n 种蔬菜,小 N 需要根据不同蔬菜的特性,综合考虑各 方面因素,设计合理的销售方案,以获得最多的收益. 在计算销售蔬菜的 ...

  7. BZOJ.4946.[NOI2017]蔬菜(贪心 离线)

    题目链接 因为有删除,考虑倒序处理某个p的询问. 那么每天删除xi的蔬菜就变成了每天运来xi的蔬菜.那么我们取当前最优的即可,早取晚取都一样,不需要留给后面取,还能给后面更优的留出空间. 这样就只需考 ...

  8. [NOI2017]蔬菜

    [NOI2017]蔬菜 题目描述 大意就是有\(n\)种物品,第\(i\)个物品有\(c_i\)个,单价是\(a_i\).然后每天你可以卖出最多\(m\)个物品.每天结束后第\(i\)种物品会减少\( ...

  9. 4946: [Noi2017]蔬菜

    4946: [Noi2017]蔬菜 http://www.lydsy.com/JudgeOnline/upload/Noi2017D2.pdf 分析: 贪心. 首先可以将一个蔬菜拆成两个,一个是有加成 ...

随机推荐

  1. Asp.net webform scaffolding结合Generic Unit of Work & (Extensible) Repositories Framework代码生成向导

    Asp.net webform scaffolding结合Generic Unit of Work & (Extensible) Repositories Framework代码生成向导 在上 ...

  2. 分布式版本控制系统GIT的使用

    一.什么是Git Git是一个分布式版本控制系统,Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异(如CVS.Subver ...

  3. MQTT再学习 -- MQTT 客户端源码分析

    MQTT 源码分析,搜索了一下发现网络上讲的很少,多是逍遥子的那几篇. 参看:逍遥子_mosquitto源码分析系列 参看:MQTT libmosquitto源码分析 参看:Mosquitto学习笔记 ...

  4. 菜鸟入门【ASP.NET Core】6:配置的热更新、配置的框架设计

    配置的热更新 什么是热更新:这个词听着有点熟悉,但到底是什么呢? 一般来说:创建的项目都无法做到热更新:即项目无需重启,修改配置文件后读取到的信息就是修改配置之后的 我们只需要吧项目中用到的IOpti ...

  5. 用idea搭建SSM项目,原来这么简单

    目录 前言 软件环境 创建项目 数据库文件 配置文件 pom.xml log4j.properties jdbc.properties applicationContext.xml spring-mv ...

  6. fllume 入门

    flume flume 简介及核心概念 什么是flume Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,目前是Apache的顶级项目.Flume支持 ...

  7. 【Java基础】2、Java中普通代码块,构造代码块,静态代码块区别及代码示例

    Java中普通代码块,构造代码块,静态代码块区别及代码示例.Java中普通代码块,构造代码块,静态代码块区别及代码示例 执行顺序:静态代码块>静态方法(main方法)>构造代码块>构 ...

  8. EmitMapper的使用小结

    最近公司开发项目前端使用一个js框架,后端使用ef,js前台读取的json采用实体的dto来进行生成. 在网上看到了EmitMapper相对其他映射框架处理速度可以更快,就拿来用了.下面是代码中常用的 ...

  9. Python hashlib 模块

    使用 md5 加密 import hashlib m = hashlib.md5() m.update('hello world'.encode('utf-8')) # 加密的字符串需要先编码成 ut ...

  10. AppBoxPro(权限管理框架--FineUIPro基础版+工厂模式+ADO.NET+存储过程)

    FineUIPro基础版火爆来袭,特献上ADO.NET纯SQL方式AppBoxPro,希望大家能够喜欢! 下载源码请到[知识星球] https://t.zsxq.com/3rrNFyv