[bzoj3936][Noi2017]蔬菜【贪心】

【题目描述】

 http://www.lydsy.com/JudgeOnline/upload/Noi2017D2.pdf

【题解】

 这道题可以从后往前贪心。

若我们知道了第i的答案,我们只要去掉(当前个数-前i-1天能取的个数)个价值最小的蔬菜,就能得到第i-1天的答案。

所以我们现在只要求出最后一天的答案。

把每种蔬菜拆成两份,前c-1个价值为a,最后一个价值为a+s(按变质的顺序)放入优先队列中,依次取出来放入还能放的地方即可。

还能放的地方可以用并查集维护。

复杂度O(n log n)。

/* --------------
    user Vanisher
    problem bzoj-4946
----------------*/
# include <bits/stdc++.h>
# define 	ll 		long long
# define 	N 		1000100
using namespace std;
ll read(){
	ll tmp=0, fh=1; char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
	return tmp*fh;
}
struct node{
	ll num,w,d;
};
bool operator <(node x, node y){
	return x.w<y.w;
}
priority_queue <node,vector<node> > hp; 
ll n,m,k,p[N],mp[N],mx,f[N],num[N],cnt,ans[N];
ll dad(ll x){
	if (x>100000) return dad(100000);
	if (f[x]==x) return x;
	else return f[x]=dad(f[x]);
}
int main(){
	n=read(), m=read(), k=read();
	for (ll i=1; i<=n; i++){
		ll a=read(), s=read(), c=read(), x=read(),ti;
		if (x!=0){
			ti=(c-1)/x+1;
			hp.push((node){1,s+a,-ti}); hp.push((node){c-1,a,x});
		}
		else {
			hp.push((node){1,s+a,-100000});
			hp.push((node){c-1,a,0});
		}
	}
	mx=100000;
	for (ll i=1; i<=k; i++) p[i]=read(),mx=max(p[i],mx);
	for (ll i=1; i<=mx; i++) f[i]=i, num[i]=m;
	while (hp.size()!=0){
		node now=hp.top();
		hp.pop();
		if (now.d<0){
			ll p=dad(-now.d);
			if (p==0) continue;
			num[p]--; mp[++cnt]=now.w;
			if (num[p]==0) f[p]=dad(f[p-1]);
			ans[mx]=ans[mx]+now.w;
		}
		else {
			ll p,nownum,newp;
			if (now.d!=0) p=(now.num-1)/now.d+1,nownum=(now.num-1)%now.d+1;
				else p=100000, nownum=now.num;
			newp=dad(p), nownum=nownum+(p-newp)*now.d; p=newp;
			while (p>0){
				if (nownum==0&&now.d==0) break;
				ll del=min(num[p],nownum);
				for (ll i=1; i<=del; i++) mp[++cnt]=now.w, ans[mx]=ans[mx]+now.w;
				nownum=nownum-del; num[p]=num[p]-del;
				if (num[p]==0) f[p]=dad(p-1);
				newp=dad(f[p-1]);
				nownum=nownum+(p-newp)*now.d;
				p=newp;
			}
		}
	}
	sort(mp+1,mp+cnt+1);
	ll tot=mx*m,now=ans[mx],l=1;
	for (ll i=mx-1; i>=1; i--){
		tot=tot-m;
		while (cnt-l+1>tot)
			now=now-mp[l++];
		ans[i]=now;
	}
	for (ll i=1; i<=k; i++)
		printf("%lld\n",ans[p[i]]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值