[关闭]
@ysner 2018-10-06T07:52:17.000000Z 字数 3068 阅读 1746

[NOI2017]蔬菜

贪心 并查集


题面

戳我

解析

一道质量不错的贪心题。
我一开始的思路就是删掉菜这个操作太麻烦了,从后往前加菜会方便很多。
于是从往前推,每天加上在当天腐烂完毕的蔬菜,然后拿个堆贪心地取,就可以得到卖天的答案。
当然要注意每天处理的是前面天的购买情况(而不是只有第天腐烂的)。
然后再从后往前推答案,由于贪心过程中第天买的蔬菜实际上可以在中任意一天买,所以每天删掉价值前(可能不满)小的蔬菜即可,这个可以用堆维护。
复杂度

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<vector>
  4. #include<queue>
  5. #define pb(a) push_back(a)
  6. #define ll long long
  7. #define re register
  8. #define il inline
  9. using namespace std;
  10. const int N=1e5+100;
  11. int n,m,k,a[N],s[N],c[N],x[N],use[N],sta[N],top,num;
  12. ll ans[N];
  13. struct dat
  14. {
  15. int w,id;
  16. il bool operator < (const dat &o) const {return w<o.w;}
  17. il bool operator > (const dat &o) const {return w>o.w;}
  18. };
  19. priority_queue<dat>Q;
  20. priority_queue<dat,vector<dat>,greater<dat> >Q1;
  21. vector<int>v[N];
  22. il int min(re int x,re int y){return x<y?x:y;}
  23. il int max(re int x,re int y){return x>y?x:y;}
  24. il int gi()
  25. {
  26. re int x=0,t=1;
  27. re char ch=getchar();
  28. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  29. if(ch=='-') t=-1,ch=getchar();
  30. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  31. return x*t;
  32. }
  33. int main()
  34. {
  35. n=gi();m=gi();k=gi();
  36. for(int i=1;i<=n;++i)
  37. {
  38. a[i]=gi(),s[i]=gi(),use[i]=c[i]=gi(),x[i]=gi();
  39. if(!x[i]) v[(int)1e5].pb(i);
  40. else v[min((int)1e5,(c[i]+x[i]-1)/x[i])].pb(i);
  41. }
  42. re ll res=0,now=1e5,tot;
  43. while(now)
  44. {
  45. for(re int i=0;i<v[now].size();++i) Q.push((dat){a[v[now][i]]+s[v[now][i]],v[now][i]});
  46. tot=m;
  47. while(tot)
  48. {
  49. if(Q.empty()) break;
  50. re dat t=Q.top();Q.pop();
  51. if(use[t.id]==c[t.id])
  52. {
  53. --use[t.id];--tot;
  54. res+=t.w;
  55. if(use[t.id]) Q.push((dat){a[t.id],t.id});
  56. }
  57. else
  58. {
  59. re ll can_=min(tot,use[t.id]-x[t.id]*(now-1));can_=max(can_,0ll);
  60. res+=can_*t.w;
  61. tot-=can_;use[t.id]-=can_;
  62. if(use[t.id]) sta[++top]=t.id;
  63. }
  64. }
  65. for(int i=1;i<=top;++i) Q.push((dat){a[sta[i]],sta[i]});top=0;
  66. --now;
  67. }
  68. for(int i=1;i<=n;++i)
  69. {
  70. if(use[i]==c[i]-1) Q1.push((dat){a[i]+s[i],i});
  71. else if(use[i]!=c[i]) Q1.push((dat){a[i],i});
  72. num+=c[i]-use[i];
  73. }
  74. ans[(int)1e5]=res;
  75. for(int o=1e5-1;o;--o)
  76. {
  77. while(num>o*m)
  78. {
  79. re dat t=Q1.top();Q1.pop();
  80. re ll los=min(c[t.id]-use[t.id]-1,num-o*m);
  81. if(use[t.id]==c[t.id]-1)
  82. {
  83. res-=t.w;--num;++use[t.id];
  84. }
  85. else if(use[t.id]<c[t.id]-1)
  86. {
  87. res-=t.w*los;num-=los;use[t.id]+=los;
  88. if(use[t.id]<c[t.id]-1) Q1.push((dat){a[t.id],t.id});
  89. else Q1.push((dat){a[t.id]+s[t.id],t.id});
  90. }
  91. }
  92. ans[o]=res;
  93. }
  94. for(int i=1;i<=k;++i) printf("%lld\n",ans[gi()]);
  95. return 0;
  96. }

然而其实没必要那么麻烦。
题目数据范围其实不够大,例如蔬菜最多卖份,这意味着我们可以用更轻松愉快的方法水过这道题。
我们其实可以直接统计卖的第份是哪种菜。
这个也可以按照价格贪心,用堆维护。
每次从它腐烂的最后一天开始卖(可以证明这样不会更劣),如果那天有份了就把它的并查集父亲赋为前一天。(这个和“疯狂的馒头”套路是一样的)
这样复杂度差不多,但常数更小代码更短,简直人赢

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #define ll long long
  5. #define re register
  6. #define il inline
  7. #define P pair<int,int>
  8. using namespace std;
  9. const int N=1e5+100;
  10. int n,m,k,a[N],s[N],c[N],x[N],mx=1e5,res[N],f[N],p;
  11. ll ans[N*10];
  12. priority_queue<P>Q;
  13. il int find(re int x){return x==f[x]?x:f[x]=find(f[x]);}
  14. int main()
  15. {
  16. ios::sync_with_stdio(false);
  17. cin>>n>>m>>k;
  18. for(re int i=1;i<=n;++i) cin>>a[i]>>s[i]>>c[i]>>x[i],Q.push(P(a[i]+s[i],i));
  19. for(re int i=1;i<=mx;++i) f[i]=i,res[i]=m;
  20. re int now=0;
  21. while(!Q.empty())
  22. {
  23. re int i=Q.top().second,w=Q.top().first,fa=find(x[i]?min(mx,(c[i]+x[i]-1)/x[i]):mx);
  24. if(!fa) {Q.pop();continue;}
  25. --res[fa];if(!res[fa]) f[fa]=find(fa-1);
  26. --c[i];if(!c[i]) Q.pop();else if(w>a[i]) Q.pop(),Q.push(P(a[i],i));
  27. ++now;ans[now]=ans[now-1]+w;
  28. }
  29. while(k--) cin>>p,printf("%lld\n",ans[min(p*m,now)]);
  30. return 0;
  31. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注