首页 > 其他分享 >NOI2017 蔬菜

NOI2017 蔬菜

时间:2024-03-30 10:22:21浏览次数:16  
标签:变质 int NOI2017 push 蔬菜 sel id nw

传送门

NOI 的题果然是非常的难且有意思。还有就是推荐一下command_block 的题解

这题的题意比较难。

题意:有 \(n\) 种菜,初始每种菜有 \(c_i\) 个,单价 \(a_i\),如果不出售每天会变质 \(x_i\) 棵。第一次卖这种菜会获得 \(s_i\) 的奖励。每天至多卖 \(m\) 菜。
给出 \(q\) 次询问,每次询问 \(1\sim q_i\) 天后的最大获利。

这里对变质的理解很重要。

变质的错误理解:无论如何,每天都必然有 \(x_i\) 个变质。
变质的正确理解:每天可以卖掉一些原本今天会变质的蔬菜,这样今天变质的蔬菜数量就 \(<x_i\) 了。

理解完题意,接下来看题解。

算法:模拟费用流。

【费用流模型】

蔬菜减少不太好,考虑时光倒流,从后往前做。每天会加入一些原来会变质的菜,菜可以无限保存。

把每个菜 \(i\) 每天 \(t\) 的状态抽象为 \((t,i)\)。

\(S\rightarrow (t,i)\),费用 \(a_i\) 容量为第 \(t\) 天新增的菜 \(i\) 数量。这个数具体是多少先放着。

\((t,i)\rightarrow (t+1,i)\),费用 \(0\) 容量 \(+\infty\),表示菜可以无限保存。

\((t,i)\rightarrow T_t\),费用 \(0\) 容量 \(+\infty\)。

\(T_t\rightarrow T\),费用 \(0\) 容量 \(m\),即 \(T_t\) 是负责收取第 \(t\) 天所有售出的菜的结点。

↑ 大概这样。

【一次模拟找性质】

因为图很复杂,考虑增量式分析。

借个 command_block 的图。

不断增加 \(T_i\) 点。

图 II 是新增的点和边,导致了图 III 的三种增广路。接下来是找环。

图 V 的环上每一条边费用都是 \(0\),没有用。

图 IV 的环的费用相当于一条边的费用减去另一条边,不如直接增广路,没有用。

然后怎么做?

上面这个图只能求一个后缀,不能求前缀。

【关注性质】

注意到上面图的一个性质:与 \(S\) 相连的边永不会退流。

这可以推出一个性质:第 \(p\) 天的最优决策包含第 \(p-1\) 天的最优决策。

具体一点,第 \(p-1\) 天最优决策卖的菜是第 \(p\) 天的子集。

进一步可以得到:假如知道了第 \(p\) 天卖了什么菜,第 \(p'(p'<p)\) 天的最优决策就是取这些菜中前 \(p'\times m\) 大的。(记得 \(m\) 是每天卖菜的最大数量)

现在的问题就是:怎么求出第 \(+\infty\) 天的最优决策。(其实不用 \(+\infty\),求出所有询问天数的最大值的最优决策即可)

【二次模拟求方案】

现在的问题是求出前 \(p\) 天的最优方案。因为现在只要求一个天数的答案,没有时间顺序的限制,所以可以随心所欲处理图。

变成添加一列点。

图 II,图 III 是两条增广路,具体意义为:把今天的菜今天卖/把过去的菜今天卖。

图 IV 图 V 的两个环类似上面的分析,都没有用。

所以我们维护图 II,III 的增广路就行了。

【Code】

点击查看代码
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 1e5 + 5;

int n, m, Q; //蔬菜种类,每天可卖个数,询问数
int a[N], s[N], c[N], d[N]; //单价,首次奖励,初始个数,变质个数
int qt[N]; //询问

int mxt = -1; //最晚天数
vector<int> add[N]; //add[i]保存所有在第i天第一次出现的菜

int sel[N] = {}; //set[i]表示此时菜i卖了多少个

typedef pair<int, int> pii;
#define mk_pr make_pair
priority_queue<pii> q;

ll ans[N * 10], cur = 0;

int main() {
    cin >> n >> m >> Q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i] >> s[i] >> c[i] >> d[i];
    }
    for (int i = 1; i <= Q; i++)
        cin >> qt[i], mxt = max(mxt, qt[i]);
    for (int i = 1; i <= n; i++) {
        if (d[i] == 0)
            add[mxt].push_back(i);
        else
            add[min((c[i] + d[i] - 1) / d[i], mxt)].push_back(i);
    }

    vector<int> v; //v保存当天会增加的菜
    for (int nw = mxt; nw >= 1; nw--) { //从后往前
        //第i种菜应有c[i]-d[i]*(nw-1)-sel[i]个
        for (int i = 0; i < add[nw].size(); i++)
            v.push_back(add[nw][i]);
        for (int i = 0; i < v.size(); i++) {
            if (sel[v[i]] == 0)
                q.push(mk_pr(a[v[i]] + s[v[i]], v[i]));
            else
                q.push(mk_pr(a[v[i]], v[i]));
        }
        v.clear();
        int cnt = m;
        while (cnt) { //只要还有得卖
            if (q.empty()) //没菜了
                break;
            pii tmp = q.top();
//            q.pop();
            int id = tmp.second;
            if (sel[id] == 0) { //还没卖过
                sel[id] = 1;
                cnt--;
                q.pop();
                if (c[id] - sel[id] - (nw - 1) * d[id] > 0) //还有
                    q.push(mk_pr(a[id], id));
                else if (d[id]) //这一次没了,但下一天会加回来
                    v.push_back(id);
                else //永远没了
                    ;
            }
            else {
                int nw_sel = min(cnt, c[id] - sel[id] - (nw - 1) * d[id]); //能卖就卖
                cnt -= nw_sel;
                sel[id] += nw_sel;
                if (c[id] - sel[id] - (nw - 1) * d[id] > 0)
                    q.push(mk_pr(a[id], id)); //没卖完呢
                else if (c[id] - sel[id] - (nw - 1) * d[id] == 0) {
                    q.pop();
                    if (d[id])
                        v.push_back(id); //还有新增
                    else
                        ; //永远没了
                }
            }
        }
    }
    //此时得到了sel[i]:每种菜卖了多少个
    //求ans序列:有哪些收益的菜卖了
    for (int i = 1; i <= n; i++) {
        if (sel[i]) {
            ans[++cur] = a[i] + s[i];
            for (int j = 2; j <= sel[i]; j++)
                ans[++cur] = a[i];
        }
    }
    sort(ans + 1, ans + cur + 1);
    reverse(ans + 1, ans + cur + 1);
    for (int i = 2; i <= cur; i++)
        ans[i] += ans[i - 1];
    for (int i = 1; i <= Q; i++)
        cout << ans[min(qt[i] * m * 1ll, cur)] << endl;
    return 0;
}

标签:变质,int,NOI2017,push,蔬菜,sel,id,nw
From: https://www.cnblogs.com/FLY-lai/p/18104979

相关文章

  • [AH2017HNOI2017] 礼物(fft)
    [AH2017/HNOI2017]礼物(fft)题目描述我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有\(n\)个装饰物,并且每个装饰物都有一定的亮度。但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且......
  • #分块,二分#洛谷 5356 [Ynoi2017] 由乃打扑克
    题目支持区间加和区间查询第\(k\)小分析分块之后给每个整块排序,这样修改的时候整块打标记,散块直接分开把需要加的部分暴力加之后归并,就是\(O(\sqrt{n})\)的查询的话,如果只有散块直接归并出结果,否则二分答案,加上小块合并成的整块,相当于是整体二分,就是\(O(\sqrt{n}\log{a_......
  • react tips/webpack热更新原理/webpack优化性能/超级蔬菜配比
    《react使用小技巧》https://www.yuque.com/beilo/simpread/1706613177588《webpack热更新原理》https://github.com/febobo/web-interview/issues/126WebpackCompile(webpack编译)BundleServer(静态资源服务器,一般是dist/build文件夹HMRServer(热更新服务器HMRRuntime(......
  • HNOI2017影魔题解
    HNOI2017影魔对于两种贡献,都只用考虑左边第一个比自己大的,和右边第一个比自己大的数,分别记为\(l_i、r_i\)对于询问一,每个数对\((l_i,r_i)\)构成全部情况对于询问二,可以拆分成\(x=l_i\)时,\(y\in[i+1,r_i-1]\),以及\(y=r_i\)时,\(x\in[l_i+1,i-1]\)我们考虑用扫描线......
  • P5365 SNOI2017 英雄联盟
    P5365SNOI2017英雄联盟基本思路刚洗完澡做的,脑子转不动了。疑似开始自动化思考了,状态转移方程是这一坨\(F[i][j]*=F[i-1][j-k*w[i]]\)事实上根本不对。首先当前的方案数完全没有体现出来,只乘了之前的方案数,而且这是一个最优性问题,不是计数问题,要在两种状况中做出选......
  • P3722 [AH2017/HNOI2017] 影魔
    题目链接Part1先想暴力,对于每次询问,可以直接\(\Theta(n^2)\)枚举数对,用\(ST\)表判断一下,复杂度为\(\Theta(qn^2)\)。发现枚举数对没有前途,考虑\((i,j)\)之间的最大值,发现一个数对产生的贡献只和区间的最大值有关,我们从这个最大值入手。我们把一个数对的贡献看做其区间最......
  • P5268 [SNOI2017] 一个简单的询问
    一个简单的询问显然这个询问并不简单如果做过莫比乌斯反演入门题problemb就会想到利用容斥将询问拆成四个那么我们现在的问题变成如何求[1,l][1,r]两个区间之间的答案,那么也是直接用莫队即可,只是维护的是两个区间的右端点,和原来的莫队有一些不一样,但是大体相同。#include<......
  • P3825 [NOI2017] 游戏 题解
    P3825[NOI2017]游戏题解首先解决没有x的情况,这种情况下每个事件有两种选择,例如a可以选择b,c,所以这就是一个2-SAT问题,但是这题比较特殊,除了题目中给的命题,还需要建立原命题的逆否命题所对应的边,最后跑一遍\(\text{Tarjan}\)就出解了。考虑有\(d\)个\(x\)的情况......
  • 食品蔬菜出口美国FDA食品注册须知
    美国食品和药物管理局(FDA)是负责监管美国食品、药品、化妆品、医疗器械和辐射产品安全的机构。在美国,任何想要将食品产品销售到市场上的企业都需要进行FDA食品注册,以确保其产品符合相关的标准和法规。首先,申请者需要确定自己是否属于FDA规定的食品注册范围。按照FDA的定义,食品包括任......
  • 各种瓜果、蔬菜种植
     种类催芽方法打芽、整枝时间成熟甜瓜种子用40度左右温水浸泡4-6小时至自然冷去,捞出沥干,用干净湿巾包好,置于25-30度处进行催芽,种子发芽期间每12小时用清水冲洗一次,以防腐烂。露白后即可播种备注:(我自己用温水跑了6个小时,然后直接种植)甜瓜秧在长到三片真叶时,就要......