zoukankan      html  css  js  c++  java
  • P3826 [NOI2017]蔬菜

    传送门

    注意每一单位蔬菜的变质时间是固定的,不随销售发生变化

    固定的......

    就是每一个单位的蔬菜在哪一天变质是早就定好了的

    发现从第一天推到最后一天很不好搞

    考虑反过来,从最后一天推到第一天,这样就相当于每天多一些蔬菜

    不管现在怎么卖都不会影响下一天多的蔬菜,不会出现贵的留到后面卖的操作,因为后面蔬菜只会越来越多,不如把空间留给之后可能会出现的更好的菜

    可以直接贪心了,每天都卖最贵的 $m$ 单位蔬菜,这样就一定是最优的

    然后就是维护每天的各种菜了,开个堆并维护一堆东西,没什么思维...

    但是这样还不够,有多组询问

    继续倒过来考虑,每天菜只会越来越多,发现 卖 $p$ 天的任意一种操作 卖 $p-1$ 天都能做

    唯一的区别只是卖 $p-1$ 天少卖了几个菜

    所以可以先处理出 $p=10^5$ 时的答案并把各种操作存一下,然后扔掉价值最小的几种操作直到总操作数在 $p-1$ 天也能做出,就能从 $p$ 推到 $p-1$ 了

    然后就可以预处理出所有的询问了

    具体维护的时候其实并不容易......看代码注释吧

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7;
    int n,m,K,a[N],s[N],c[N],x[N],sum[N];
    //n,m,k,a,s,c,x意义同题面,sum[i]维护当前编号为i的菜卖出的数量
    bool fir[N];//判断是否是第一次卖
    ll ans[N];//存答案
    struct dat1{
        int val,id;//蔬菜的价值,编号
        inline bool operator < (const dat1 &tmp) const {
            return val<tmp.val;
        }
    }tmp[17];//队列,存今天卖完的菜编号
    priority_queue <dat1> Q1;//维护当前各种存在的蔬菜
    struct dat2{
        int val,id;//卖的菜的价值,编号
        inline bool operator < (const dat2 &tmp) const {
            return val>tmp.val;
        }
    };
    priority_queue <dat2> Q2;//维护当前卖的各种菜
    vector <int> V[N];//V[i][j]维护在第i天 第一次出现的 第j种菜的编号
    void pre_work()
    {
        for(int i=1;i<=n;i++)
        {
            if(x[i]&& c[i]/x[i]+(c[i]%x[i]!=0)<=1e5 )//注意特判
                V[ c[i]/x[i]+(c[i]%x[i]!=0) ].push_back(i);
            else Q1.push((dat1){a[i]+s[i],i});//先加入第一次卖的价值
        }
        for(int now=100000;now>0;now--)//从最后一天考虑
        {
            for(int i=V[now].size()-1;i>=0;i--) Q1.push((dat1){ a[V[now][i]]+s[V[now][i]] , V[now][i] });
            //把每天的新菜加入堆,价值为第一次卖的价值
            int cnt=0;//tmp队列头
            for(int t=0;t<m&&!Q1.empty();)//t记录今天已经卖了多少菜
            {
                dat1 y=Q1.top(); Q1.pop(); int z=y.id;//找出价值最大的菜
                if(fir[z])//不是第一次卖
                {
                    int num=min(m-t/*最多能卖的量*/, c[z]-(now-1)*x[z]-sum[z]/*当前此菜的量*/ );
                    sum[z]+=num; t+=num; ans[100000]+=1ll*num*y.val;//注意long long
                    if(sum[z]!=c[z]) tmp[++cnt]=y;//如果没完全卖完就加入队列(今天的量已经完了)
                }
                else//第一次卖
                {
                    sum[z]++; t++; ans[100000]+=y.val; fir[z]=1;//只卖一单位
                    if(sum[z]!=c[z]) Q1.push((dat1){a[z],z});//把非第一次卖的价值加入堆
                }
            }
            for(int j=1;j<=cnt;j++) Q1.push(tmp[j]);//下一天了,把今天卖完的菜补充回来
        }
        int tot=0;//记录卖的总数
        for(int i=1;i<=n;i++)//记录各种操作
        {
            if(sum[i]==1) Q2.push((dat2){a[i]+s[i],i});//特判第一次
            else if(sum[i]) Q2.push((dat2){a[i],i});
            tot+=sum[i];
        }
        for(int i=1e5-1;i;i--)//逆推ans
        {
            ans[i]=ans[i+1]; if(tot<=m*i) continue;//卖的数量合法就不用少卖
            for(int t=0;t<tot-m*i&&!Q2.empty();)//t记录当前少卖的数量
            {
                dat2 y=Q2.top(); Q2.pop(); int z=y.id;//把价值最小的操作拿出
                if(sum[z]!=1)//不是第一次
                {
                    int num=min(sum[z]-1/*刚好减少到第一次*/,tot-m*i-t/*刚好合法*/);
                    sum[z]-=num; t+=num; ans[i]-=1ll*num*y.val;
                    if(sum[z]==1) Q2.push((dat2){a[z]+s[z],z});//剩下一单位,把第一次的价值加入
                    else Q2.push((dat2){a[z],z});//还不止一单位
                }
                else sum[z]--,t++,ans[i]-=y.val;//第一次卖
            }
            tot=m*i;//更新tot
        }
    }
    int main()
    {
        n=read(),m=read(),K=read();
        for(int i=1;i<=n;i++) a[i]=read(),s[i]=read(),c[i]=read(),x[i]=read();
        pre_work();
        while(K--) printf("%lld
    ",ans[read()]);
        return 0;
    }
  • 相关阅读:
    Windows 10 字体替换
    Windows 任务栏增加秒显示
    CCProxy v8.0 代理服务器
    Git RPM软件包 && 源码包
    Google Chrome 浏览器最新版本 71.0.3578.98(正式版本) (64 位)
    Linux 查询公网出口IP
    Windows 10 Enterprise LTSC做Windows To Go蓝屏无法进入系统
    Nginx编译./configure翻译
    Windows 10 快捷键
    JDBC连接MySQL
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11169606.html
Copyright © 2011-2022 走看看