P3826 [NOI2017]蔬菜
题意:有n中蔬菜,第i中蔬菜单价为 a i a_i ai,每次卖出一种以前没卖出过的蔬菜时,会有 s i s_i si的额外收入,一开始的每种蔬菜的库存为 c i c_i ci个单位,每天会坏掉 x i x_i xi个单位的蔬菜。每天最多能卖出m个单位的蔬菜,给出k个询问,问第P天最多能够获得的价值
正常思路:先不考虑额外的收入,我们每天肯定是选择最贵的菜卖,每次当有一种蔬菜要消失的时候,去考虑一下卖出这种蔬菜,获得的额外价值。但是有k个询问,并不好处理。
思路:逆向考虑,从最后一天往前推,那么每往前一天都会有新的蔬菜,每一天都是选择最贵的菜卖出去
1、首先枚举蔬菜,看蔬菜最后一天出现在哪,
x
i
x_i
xi为0的蔬菜认为存在在任意一天
2、然后从后往前枚举第几天,把这一天的蔬菜放入堆中,取最贵的蔬菜卖出。卖出的时候要区分是否为第一次卖出,第一次卖出价格为
a
[
i
]
+
s
[
i
]
a[i]+s[i]
a[i]+s[i],否则为
a
[
i
]
a[i]
a[i]
3、但是有k次询问,所以我们从最后一天往前递推答案,前一天比后一天少卖出m个蔬菜,那么我们就从后一天中选择价值最低的m个蔬菜弹出,在弹出蔬菜的时候,如果该蔬菜已经卖出的数量大于1,那么分两段来弹出,至少保留一个,然后再把剩余的蔬菜放入堆中
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d) (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;
vector<int> date[maxn];
bool used[maxn];
ll sold[maxn],a[maxn],s[maxn],c[maxn],x[maxn],ans[maxn];
int n,m,k,p=1e5;
queue<int> reuse;
struct Vegetable
{
ll price;
int no;
bool operator<(const Vegetable& b) const
{
return price<b.price;
}
};
struct cmp
{
bool operator()(const Vegetable&a,const Vegetable&b) const
{
return a.price>b.price;
}
};
priority_queue<Vegetable> pq;
priority_queue<Vegetable,vector<Vegetable>,cmp> pq2;
int main()
{
scanf("%d%d%d",&n,&m,&k);
rep(i,1,n)
scanf("%d%d%d%d",&a[i],&s[i],&c[i],&x[i]);
rep(i,1,n)
{
if(x[i]==0)
date[p].pb(i);
else
date[min(1ll*p,(c[i]+x[i]-1)/x[i])].pb(i);
}
for(int i=p;i>=1;--i)
{
for(int j=0;j<date[i].size();++j)
pq.push({a[date[i][j]]+s[date[i][j]],date[i][j]});
if(pq.empty())
continue;
ll goods=m;
while(goods&&!pq.empty())
{
auto it=pq.top();
if(used[it.no]==false)
{
used[it.no]=true;
ans[p]+=it.price;
sold[it.no]++;
goods--;
if(c[it.no]!=1)
pq.push({a[it.no],it.no});
}
else
{
//每次往前推,i会变小,也就是有新的蔬菜到达
ll remain=c[it.no]-sold[it.no]-(i-1)*x[it.no];
ll sell=min(remain,goods);
ans[p]+=sell*it.price;
sold[it.no]+=sell;
goods-=sell;
//如果已卖出的数量还不到库存数量,那么继续放入堆中
if(sold[it.no]!=c[it.no])
reuse.push(it.no);
}
pq.pop();
}
while(!reuse.empty())
{
auto it=reuse.front();
pq.push({a[it],it});
reuse.pop();
}
}
ll total=0;
//区分卖出一个还是多个
for(int i=1;i<=n;++i)
{
if(sold[i]==1)
pq2.push({a[i]+s[i],i});
else if(sold[i]!=0)
pq2.push({a[i],i});
total+=sold[i];
}
for(int i=p-1;i>=1;--i)
{
ans[i]=ans[i+1];
if(total<=m*i)
continue;
ll goods=total-m*i;
while(goods&&!pq2.empty())
{
auto it=pq2.top();
pq2.pop();
if(sold[it.no]!=1)
{
ll sell=min(sold[it.no]-1,goods);
sold[it.no]-=sell;
goods-=sell;
ans[i]-=sell*it.price;
if(sold[it.no]==1)
pq2.push({a[it.no]+s[it.no],it.no});
else
pq2.push({a[it.no],it.no});
}
else
{
goods--;
sold[it.no]--;
ans[i]-=it.price;
}
}
total=m*i;
}
int x;
for(int i=1;i<=k;++i)
{
scanf("%d",&x);
printf("%lld\n",ans[x]);
}
return 0;
}