前不久做了一道同样出处的题,然后发现这道也做了,居然还是黑题(很好写,但思路我想不到),就花了20分钟回顾了一下
- 题意:有n种蔬菜,每种都有,\(a_i,s_i,c_i,d_i\)分别表示卖出去一单位的价钱,第一次卖额外得的价钱,初始有多少单位,每天结束时腐烂的单位(最后一天腐烂\(c_i\)%\(d_i\))Q个询问问你\(p_i\)天结束时可得的最大收益
- 思路:贪心(堆)+并查集
两个策略:1.菜要尽量再快腐烂完时卖掉。2.优先卖贵的
用堆满足2,然后记录day[]每天剩余的空间,并查集方便快速找到i天前第一个day[]<m(没满)的一天,然后在这天卖,接着维护信息。
感性理解:相当于把卖菜方案尽量往后推,先推贵的。不过最后up=1e6>个菜卖完以后,就把所有的往前推,占满前缀连续的格子(当然这个不是操作,是原理,只需要查询[前\(p_i\)*\(m\)个的销售额即可])。
复杂度\(O(nmlogn)\) - code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n,m,k,c[N],d[N],day[N],fa[N];
ll a[N],s[N],ans[N];
int g_fa(int u) {return fa[u]=(fa[u]==u)?u:g_fa(fa[u]);}
struct node {
int x;ll w;
bool operator<(const node &u) const{return w<u.w;}
};
priority_queue<node> Q;
int main() {
// freopen("mm.in","r",stdin);
// freopen("mm.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d%d%d%d",&a[i],&s[i],&c[i],&d[i]);
for(int i=1;i<=n;i++) Q.push((node){i,s[i]+a[i]});
for(int i=0;i<=1e6;i++)fa[i]=i;
int cc=0;
while(!Q.empty()) {
int u=Q.top().x;ll w=Q.top().w; Q.pop();
int dmx=1e6;
if(d[u]) dmx=min(dmx,(c[u]+d[u]-1)/d[u]);
dmx=g_fa(dmx);
if(!dmx) continue;
day[dmx]++;if(day[dmx]==m)fa[dmx]=g_fa(dmx-1);
c[u]--;if(c[u])Q.push((node){u,a[u]});
ans[cc+1]=ans[cc]+w;cc++;
}
// printf("%d\n",cc);
for(int i=1;i<=k;i++) {
int p; scanf("%d",&p);
printf("%lld\n",ans[min(cc,p*m)]);
}
return 0;
}