[NOI2017]蔬菜

题目正着做不好做。
然而考虑逆向思维。
把时间轴反转,则问题变成了每次会运来一些菜,询问最大的价值。
先考虑如何回答一个询问。
\(p\)开始倒推。
我们当前的最高价值的菜一定会卖掉。这是因为如果拖到后面会占用更多的空间,后面很可能会有更高价值的菜运过来。
于是可以维护一个大根堆\(x\)。首先把所有当前的菜的\(a_i+s_i\)插入堆,然后每次删除最大价值的。
维护一个回收堆\(y\),表示当前还有什么菜。
在删除\(x\)的元素后,若这个元素的菜没被卖光,则把这个菜的\(a\)值插入\(y\)中。
然而后面的点数据范围为\(10^5\),上述算法无能为力。
由于\(q\)的范围为\(10^5\),且卖出天数如果相同,则答案也相同。
所以考虑求出卖出\(1\to 10^5\)天的答案。
由于前面的时间的菜的集合一定包含后面的时间的菜的集合,所以发现\(p-1\)可以选择的菜包含\(p\)可以选择的。
\(p-1\)天比\(p\)天要少卖掉的只有\(m\)个菜。
并且卖掉任意的菜都可以。
显然我们只需要不卖价值最小的\(m\)个菜即可。
在实现上,要先考虑不卖没有额外价值,再不卖有额外价值的部分。
最终时间复杂度\(O(pm\log_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\n",ans[p]);
	}
}
posted @ 2020-12-13 10:01  celerity1  阅读(79)  评论(0编辑  收藏  举报