• [NOI2017]蔬菜


    https://www.zybuluo.com/ysner/note/1301242

    题面

    戳我

    解析

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

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #define pb(a) push_back(a)
    #define ll long long
    #define re register
    #define il inline
    using namespace std;
    const int N=1e5+100;
    int n,m,k,a[N],s[N],c[N],x[N],use[N],sta[N],top,num;
    ll ans[N];
    struct dat
    {
      int w,id;
      il bool operator < (const dat &o) const {return w<o.w;}
      il bool operator > (const dat &o) const {return w>o.w;}
    };
    priority_queue<dat>Q;
    priority_queue<dat,vector<dat>,greater<dat> >Q1;
    vector<int>v[N];
    il int min(re int x,re int y){return x<y?x:y;}
    il int max(re int x,re int y){return x>y?x:y;}
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    int main()
    {
      n=gi();m=gi();k=gi();
      for(int i=1;i<=n;++i)
        {
          a[i]=gi(),s[i]=gi(),use[i]=c[i]=gi(),x[i]=gi();
          if(!x[i]) v[(int)1e5].pb(i);
          else v[min((int)1e5,(c[i]+x[i]-1)/x[i])].pb(i);
        }
      re ll res=0,now=1e5,tot;
      while(now)
      {
        for(re int i=0;i<v[now].size();++i) Q.push((dat){a[v[now][i]]+s[v[now][i]],v[now][i]});
        tot=m;
        while(tot)
        {
          if(Q.empty()) break;
          re dat t=Q.top();Q.pop();
          if(use[t.id]==c[t.id])
        {
          --use[t.id];--tot;
          res+=t.w;
          if(use[t.id]) Q.push((dat){a[t.id],t.id});
        }
          else
        {
          re ll can_=min(tot,use[t.id]-x[t.id]*(now-1));can_=max(can_,0ll);
          res+=can_*t.w;
          tot-=can_;use[t.id]-=can_;
          if(use[t.id]) sta[++top]=t.id;
        }
        }
        for(int i=1;i<=top;++i) Q.push((dat){a[sta[i]],sta[i]});top=0;
        --now;
      }
      for(int i=1;i<=n;++i)
        {
          if(use[i]==c[i]-1) Q1.push((dat){a[i]+s[i],i});
          else if(use[i]!=c[i]) Q1.push((dat){a[i],i});
          num+=c[i]-use[i];
        }
      ans[(int)1e5]=res;
      for(int o=1e5-1;o;--o)
        {
          while(num>o*m)
          {
        re dat t=Q1.top();Q1.pop();
            re ll los=min(c[t.id]-use[t.id]-1,num-o*m);
      	if(use[t.id]==c[t.id]-1)
        {
              res-=t.w;--num;++use[t.id];
        }
        else if(use[t.id]<c[t.id]-1)
        {
              res-=t.w*los;num-=los;use[t.id]+=los;
          if(use[t.id]<c[t.id]-1) Q1.push((dat){a[t.id],t.id});
          else Q1.push((dat){a[t.id]+s[t.id],t.id});
            }
          }
          ans[o]=res;
        }
      for(int i=1;i<=k;++i) printf("%lld
    ",ans[gi()]);
      return 0;
    }
    

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

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #define ll long long
    #define re register
    #define il inline
    #define P pair<int,int>
    using namespace std;
    const int N=1e5+100;
    int n,m,k,a[N],s[N],c[N],x[N],mx=1e5,res[N],f[N],p;
    ll ans[N*10];
    priority_queue<P>Q;
    il int find(re int x){return x==f[x]?x:f[x]=find(f[x]);}
    int main()
    {
      ios::sync_with_stdio(false);
        cin>>n>>m>>k;
      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));
      for(re int i=1;i<=mx;++i) f[i]=i,res[i]=m;
        re int now=0;
        while(!Q.empty())
        {
            re int i=Q.top().second,w=Q.top().first,fa=find(x[i]?min(mx,(c[i]+x[i]-1)/x[i]):mx);
            if(!fa) {Q.pop();continue;}
            --res[fa];if(!res[fa]) f[fa]=find(fa-1);
            --c[i];if(!c[i]) Q.pop();else if(w>a[i]) Q.pop(),Q.push(P(a[i],i));
            ++now;ans[now]=ans[now-1]+w;
        }
        while(k--) cin>>p,printf("%lld
    ",ans[min(p*m,now)]); 
      return 0;
    }
    
  • 相关阅读:
    js中eval详解
    javascript数组操作大全
    JavaScript面向对象编程深入分析
    IBatisNet系列二QuickStart篇
    IBatis.Net系列Mapped Statements的语法
    MonoRail学习-入门实例篇
    IBatisNet系列执行存储过程
    IBatis.Net系列多参数的SQL语句的配置
    Castle Startable Facility 简单理解
    wxPython创建系统托盘
  • 原文地址:https://www.cnblogs.com/yanshannan/p/9746542.html
Copyright © 2020-2023  润新知