zoukankan      html  css  js  c++  java
  • [NOI2017] 蔬菜

    XXXIX.[NOI2017] 蔬菜

    第一眼这个奇奇怪怪的限制,想到网络流。

    为了处理这个“每天坏 \(c_i\)”个的限制,我想到的方法是,第一天的 \(c_i\) 个仅能在第一天销售,就只往代表第一天的点连边;第二天的 \(c_i\) 个可以在第一天和第二天销售,故往代表第一天和第二天的点连边;第三天就往前三天连边……然后发现这样边数是 \(O(n^2)\) 的。

    尝试消减边数。发现这里面连了很多重复的边,而且如果我们倒着建图,令源点在第 \(\inf\) 天的位置,汇点在第 \(0\) 天的位置,就能使得边数消减到 \(O(n)\),因为这样子每天的蔬菜作用范围就是一条前缀。

    然后我们来思考这张图的实际意义——从后往前推,每天突然出现 \(c_i\) 单位的菜,并且这之后这些菜都能卖。

    稍微思考一下这个方法,就会发现一个非常棒的地方:因为任意时刻,你手头的菜在接下来的时刻也都能卖,所以就不必担心这些菜会烂在手里之类的,直接贪心地挑最贵的菜卖即可。至于那个“第一份菜补贴”的东西,我们就把它想成,某种菜出现的第一天,其中有一颗菜是镀金的,价格高一点,但是一样可以在贪心里算。

    但是这个东西的复杂度,算算是 \(O(n^2m\log n)\) 的,必须优化。

    然后就不会了

    明显,这种优化的唯一可能是从后一天的答案递推到前一天。于是我们考虑先求出第 \(\inf\) 天(实际上取第 \(10^5\) 天即可)时的答案。我们对于每天都开一个 vector。对于每种菜,我们将其编号扔到它全部消失那天的 vector 中。接着,从后往前遍历,用一个堆维护,每当访问到一天时,就将该天 vector 中所有菜,按照补贴价格扔进堆。

    接着,从堆弹出 \(m\) 个最贵的菜卖掉。具体而言,如果最贵的是补贴菜,就卖一个补贴菜,然后将平时的价格扔进堆;如果最贵的是平价菜,则目前仓库里剩余的平价菜数量,显然可以通过记录当前已经卖了多少此种菜,与当前天数来推出。推出剩余平价菜数量后,卖出 \(\min(\text{此种菜数量},\text{本日剩余卖菜名额})\) 个菜。如果发现这个菜现有的库存已经卖光了,就把它扔进一个备用数组存着,等今天的销售处理完毕后再把它扔回堆里(因为第二天还有新的菜会到货)。

    于是我们现在推出了第 \(\inf\) 日的答案,也就知道了最优的卖菜方法。我们尝试递推答案。当我们想从 \(i+1\) 日逆推出 \(i\) 日的答案时,首先先判断手头的菜数量是否不大于 \(im\)(即前 \(i\) 日所能卖出的菜数),如果是显然就不用放弃某些菜了;否则,从现行的卖菜方法中不断扔出最便宜的菜(可以开一个小根堆维护),直到小根堆的 size 已经达到 \(im\)

    时间复杂度 \(O(nm\log n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int LIM=1e5;
    int n,m,P,pro[100100],bon[100100],amt[100100],per[100100],sel[100100],tot;
    ll res[100100];
    vector<int>v[100100],bin;
    priority_queue<pair<int,int> >q;
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >p;
    int main(){
    	scanf("%d%d%d",&n,&m,&P);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d%d%d",&pro[i],&bon[i],&amt[i],&per[i]);
    		if(!per[i])v[LIM].push_back(i);
    		else v[min((amt[i]-1)/per[i]+1,LIM)].push_back(i);
    	}
    	for(int i=LIM;i;i--){
    //		printf("%d:\n",i);
    		for(auto j:v[i])q.push(make_pair(pro[j]+bon[j],j));
    		int lim=m;
    		while(lim&&!q.empty()){
    			int x=q.top().second;q.pop();
    //			printf("%d,%d\n",x,lim);
    			if(!sel[x]){lim--,sel[x]++,res[LIM]+=pro[x]+bon[x],q.push(make_pair(pro[x],x));continue;}//first sale
    			int tmp=min(lim,amt[x]-per[x]*(i-1)-sel[x]);
    			lim-=tmp,sel[x]+=tmp,res[LIM]+=1ll*tmp*pro[x],bin.push_back(x);
    		}
    		while(!bin.empty()){
    			if(sel[bin.back()]!=amt[bin.back()])q.push(make_pair(pro[bin.back()],bin.back()));
    			bin.pop_back();
    		}
    		tot+=m-lim;
    	}
    	for(int i=1;i<=n;i++){
    		if(!sel[i])continue;
    		if(sel[i]==1)p.push(make_pair(pro[i]+bon[i],i));
    		else p.push(make_pair(pro[i],i));
    	}
    //	puts("IN");
    	for(int i=LIM;i;i--){
    //		printf("%d:\n",i);
    		res[i-1]=res[i];
    		if((i-1)*m>=tot)continue;
    		int lim=tot-(i-1)*m;tot=(i-1)*m;
    		while(lim&&!p.empty()){
    			int x=p.top().second;p.pop();
    			if(sel[x]==1){lim--,sel[x]--,res[i-1]-=pro[x]+bon[x];continue;}
    			int tmp=min(lim,sel[x]-1);
    			lim-=tmp,sel[x]-=tmp,res[i-1]-=1ll*tmp*pro[x];
    			if(sel[x]==1)p.push(make_pair(pro[x]+bon[x],x));
    			else if(sel[x])p.push(make_pair(pro[x],x));
    		}
    	}
    	for(int i=1,x;i<=P;i++)scanf("%d",&x),printf("%lld\n",res[x]);
    	return 0;
    }
    

  • 相关阅读:
    Scrum 冲刺博客第五篇
    Scrum 冲刺博客第四篇
    Scrum 冲刺博客第三篇
    ajax send()
    form action中get post传递参数的问题
    struts2 iterator中if标签的使用
    表格内容自动换行
    从js向Action传中文参数出现乱码问题的解决方法
    java开发环境搭建
    Can not find the tag library descriptor for "http://java.sun.com/jsp/jstl/
  • 原文地址:https://www.cnblogs.com/Troverld/p/14612762.html
Copyright © 2011-2022 走看看