这题很有思维难度,乍一看基本无从下手。
给每个蔬菜钦定退役的时间显然很困难,可以考虑让时光倒流,从后向前递推,然后就变成了某个时间点有一部分蔬菜服役,而已经服役的蔬菜不会退役了。然后就可以直接考虑贪心,每种第一个出现的蔬菜,显然可以单独考虑,加上s[i],然后把蔬菜放到堆里面,就可以在O(pmlogn)的复杂度下求出f[p]了,用堆维护即可,假定p=1e5。
然后发现这个玩意可以递推求解,第p-1天在役的蔬菜一定不少于第p天的,显然只需去掉利润最少的m个即可。
#include<bits/stdc++.h> #define pb push_back using namespace std; const int N=1e5+7; typedef long long ll; typedef pair<int,int>pii; struct node{int v,x;}st[N]; bool operator<(node a,node b){return a.v<b.v;} int n,m,T,P=1e5,top,sum,a[N],s[N],c[N],x[N],used[N]; ll ans[N]; bool vis[N]; vector<int>d[N]; priority_queue<node>q; int main() { scanf("%d%d%d",&n,&m,&T); for(int i=1;i<=n;i++)scanf("%d%d%d%d",&a[i],&s[i],&c[i],&x[i]); for(int i=1;i<=n;++i)if(!x[i])d[P].pb(i);else d[min(P,(c[i]+x[i]-1)/x[i])].pb(i); for(int i=P;i;i--) { for(int j=0;j<d[i].size();j++)q.push((node){a[d[i][j]]+s[d[i][j]],d[i][j]}); if(q.empty())continue; int j=m; while(j&&!q.empty()) { node u=q.top();q.pop(); if(!vis[u.x]) { vis[u.x]=1,ans[P]+=u.v,used[u.x]++,--j; if(c[u.x]>1)q.push((node){a[u.x],u.x}); } else{ int rest=min(j,c[u.x]-used[u.x]-(i-1)*x[u.x]); ans[P]+=1ll*rest*u.v,used[u.x]+=rest,j-=rest; if(used[u.x]!=c[u.x])st[++top]=(node){a[u.x],u.x}; } } while(top)q.push(st[top--]); } while(!q.empty())q.pop(); for(int i=1;i<=n;i++)sum+=used[i]; for(int i=1;i<=n;i++) if(used[i]==1)q.push((node){-s[i]-a[i],i});else if(used[i])q.push((node){-a[i],i}); for(int i=P-1;i;i--) { ans[i]=ans[i+1]; while(sum>i*m&&!q.empty()) { node u=q.top(); q.pop(),u.v*=-1; if(used[u.x]>1) { int rest=min(sum-i*m,used[u.x]-1); used[u.x]-=rest,sum-=rest,ans[i]-=1ll*rest*u.v; if(used[u.x]==1)q.push((node){-a[u.x]-s[u.x],u.x}); else q.push((node){-a[u.x],u.x}); } else sum--,used[u.x]--,ans[i]-=u.v; } } for(int i=1,x;i<=T;i++)scanf("%d",&x),printf("%lld\n",ans[x]); }View Code