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

    题目正着做不好做。
    然而考虑逆向思维。
    把时间轴反转,则问题变成了每次会运来一些菜,询问最大的价值。
    先考虑如何回答一个询问。
    (p)开始倒推。
    我们当前的最高价值的菜一定会卖掉。这是因为如果拖到后面会占用更多的空间,后面很可能会有更高价值的菜运过来。
    于是可以维护一个大根堆(x)。首先把所有当前的菜的(a_i+s_i)插入堆,然后每次删除最大价值的。
    维护一个回收堆(y),表示当前还有什么菜。
    在删除(x)的元素后,若这个元素的菜没被卖光,则把这个菜的(a)值插入(y)中。
    然而后面的点数据范围为(10^5),上述算法无能为力。
    由于(q)的范围为(10^5),且卖出天数如果相同,则答案也相同。
    所以考虑求出卖出(1 o 10^5)天的答案。
    由于前面的时间的菜的集合一定包含后面的时间的菜的集合,所以发现(p-1)可以选择的菜包含(p)可以选择的。
    (p-1)天比(p)天要少卖掉的只有(m)个菜。
    并且卖掉任意的菜都可以。
    显然我们只需要不卖价值最小的(m)个菜即可。
    在实现上,要先考虑不卖没有额外价值,再不卖有额外价值的部分。
    最终时间复杂度(O(pmlog_2n+q))
    细节看代码。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define N 100010
    int n,m,k,ans[N],a[N],s[N],c[N],x[N],vi[N],sl[N],ct;
    vector<int>v[N];
    struct n1{
    	int x,y;
    };
    int operator <(n1 x,n1 y){
    	return x.y<y.y;
    }
    struct n2{
    	int x,y;
    };
    int operator <(n2 x,n2 y){
    	return x.y>y.y;
    }
    priority_queue<n1>q1;
    priority_queue<n2>q2;
    queue<int>q;
    signed main(){
    	int p=1e5;
    	scanf("%lld%lld%lld",&n,&m,&k);
    	for(int i=1;i<=n;i++){
    		scanf("%lld%lld%lld%lld",&a[i],&s[i],&c[i],&x[i]);
    		if(!x[i])
    			v[p].push_back(i);
    		else
    			v[min(p,(c[i]+x[i]-1)/x[i])].push_back(i);
    	}
    	for(int i=p;i;i--){
    		for(int j=0;j<v[i].size();j++){
    			int x=v[i][j];
    			q1.push((n1){x,a[x]+s[x]});
    		}
    		int f=m;
    		while(!q1.empty()&&f){
    			n1 y=q1.top();
    			q1.pop();
    			if(!vi[y.x]){
    				vi[y.x]=1;
    				ans[p]+=y.y;
    				f--;
    				sl[y.x]++;
    				ct++;
    				if(c[y.x]!=1)
    					q1.push((n1){y.x,a[y.x]});
    			}
    			else{
    				int t=c[y.x]-(i-1)*x[y.x]-sl[y.x];
    				t=min(t,f);
    				ans[p]+=y.y*t;
    				f-=t;
    				sl[y.x]+=t;
    				ct+=t;
    				if(sl[y.x]!=c[y.x])
    					q.push(y.x);
    			}
    		}
    		while(!q.empty()){
    			int x=q.front();
    			q.pop();
    			q1.push((n1){x,a[x]});
    		}
    	}
    	for(int i=1;i<=n;i++){
    		if(sl[i]==1)
    			q2.push((n2){i,a[i]+s[i]});
    		else if(sl[i])
    			q2.push((n2){i,a[i]});
    	}
    	for(int i=p-1;i;i--){
    		ans[i]=ans[i+1];
    		if(ct<=i*m)
    			continue;
    		int f=ct-i*m;
    		while(!q2.empty()&&f){
    			n2 y=q2.top();
    			q2.pop();
    			if(sl[y.x]==1){
    				sl[y.x]=0;
    				f--;
    				ans[i]-=y.y;
    			}
    			else{
    				int t=min(f,sl[y.x]-1);
    				f-=t;
    				sl[y.x]-=t;
    				ans[i]-=t*y.y;
    				if(sl[y.x]==1)
    					q2.push((n2){y.x,s[y.x]+a[y.x]});
    				else
    					q2.push((n2){y.x,a[y.x]}); 
    			}
    		}
    		ct=i*m;
    	}
    	for(int i=1;i<=k;i++){
    		int p;
    		scanf("%lld",&p);
    		printf("%lld
    ",ans[p]);
    	}
    }
    
  • 相关阅读:
    分布式系统之CAP原理
    分布式缓存一致性哈希算法
    数据库三范式 无重复列 完全依赖主键 属性不依赖非主属性
    二叉树 B-树B+树
    数据库索引 主键 聚集索引 非聚集索引
    数据库水平拆分
    线程池ScheduledThreadPoolExecutor
    线程池之ThreadPoolExecutor
    mybatis一级缓存和二级缓存
    vue框架的搭建
  • 原文地址:https://www.cnblogs.com/ctmlpfs/p/14127927.html
Copyright © 2011-2022 走看看