• [NOI2017]蔬菜(贪心)


    神仙题啊!

    早上开了两个多小时,终于肝出来了,真香

    我们考虑从第 (10^5) 天开始递推,先生成 (p=10^5) 的解,然后逐步推出 (p-1,...,2,1) 的解。

    那怎么推出 (p=10^5) 的解呢?

    现在将题目转化成不停进货然后取 (m) 个最大的问题,然后删除最大并 (push) 进去。这我们可以想到大根堆。

    但是由于政策 增加题目难度,有一个 (s)。我们需要先将 ((a_i+s_i,i)) (push) 进去,然后用掉了以后将 ((a_i,i)) (push) 进去。

    因为在第 (i) 天买了一种蔬菜,当天就无需再用,我们考虑用一个栈存储一下当天用了不是 (a+s) 的蔬菜,

    然后 (p=10^5) 的解生成完了以后,现在就是生成其他解了。

    我们每次将最小的 (sum-i*m) 个取出然后删除。此时我们也要特判政策。

    答案要开 (long long),时间复杂度 (O(pmlog n))

    (Code Below:)

    #include <bits/stdc++.h>
    #define ll long long
    #define pii pair<int,int>
    #define mp make_pair
    #define F first
    #define S second
    using namespace std;
    const int maxn=100000+10;
    const int lim=100000;
    int n,m,k,a[maxn],s[maxn],c[maxn],x[maxn],used[maxn],sum,top;
    vector<int> v[maxn];ll ans[maxn];pii sta[maxn];
    priority_queue<pii> pq;
    
    inline int read(){
    	register int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return (f==1)?x:-x;
    }
    
    int main()
    {
    	n=read(),m=read(),k=read();
    	for(int i=1;i<=n;i++){
    		a[i]=read(),s[i]=read(),c[i]=read(),x[i]=read();
    		if(x[i]==0) v[lim].push_back(i);
    		else v[(c[i]+x[i]-1)/x[i]].push_back(i);
    	}
    	pii u;int rest;
    	for(int i=lim;i>=1;i--){
    		for(int j=0;j<v[i].size();j++) pq.push(mp(a[v[i][j]]+s[v[i][j]],v[i][j]));
    		for(int j=m;j>=1&&!pq.empty();){
    			u=pq.top(),pq.pop();
    			if(!used[u.S]){
    				used[u.S]++;ans[lim]+=u.F;j--;
    				if(used[u.S]!=c[u.S]) pq.push(mp(a[u.S],u.S));
    			}
    			else {
    				rest=min(j,c[u.S]-used[u.S]-(i-1)*x[u.S]);
    				ans[lim]+=(ll)rest*a[u.S];used[u.S]+=rest;j-=rest;
    				if(used[u.S]!=c[u.S]) sta[++top]=mp(a[u.S],u.S);
    			}
    		}
    		while(top) pq.push(sta[top--]);
    	}
    	while(!pq.empty()) pq.pop();
    	for(int i=1;i<=n;i++) sum+=used[i];
    	for(int i=1;i<=n;i++){
    		if(used[i]==1) pq.push(mp(-a[i]-s[i],i));
    		else if(used[i]) pq.push(mp(-a[i],i));
    	}
    	for(int i=lim-1;i>=1;i--){
    		ans[i]=ans[i+1];
    		while(sum>i*m&&!pq.empty()){
    			u=pq.top(),pq.pop();u.F=-u.F;
    			if(used[u.S]==1) ans[i]-=u.F,sum--,used[u.S]=0;
    			else {
    				rest=min(sum-i*m,used[u.S]-1);
    				sum-=rest;used[u.S]-=rest;ans[i]-=(ll)rest*a[u.S];
    				if(used[u.S]==1) pq.push(mp(-a[u.S]-s[u.S],u.S));
    				else pq.push(mp(-a[u.S],u.S));
    			}
    		}
    	}
    	while(k--) printf("%lld
    ",ans[read()]);
    	return 0;
    }
    
  • 相关阅读:
    流 例题

    容器集合整理
    容器集合
    容器 集合知识点
    面向对象回顾
    面向对象例题
    Java常用的8大排序算法
    Java中两个动态代理
    为什么synchronized无法禁止指令重排,却能保证有序性
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/10253806.html
Copyright © 2020-2023  润新知