首页 > 技术文章 > 「NOI2017」蔬菜

zsbzsb 2020-06-16 20:30 原文

传送门

考虑一个比较常见的贪心套路:我们肯定想把利润高的蔬菜在它坏掉之前卖掉,而且我们最好是卡着它变质的日子卖,这样就可以给别的蔬菜尽可能地提供卖出去的可能。

不难发现因为天数是无限多的,所以我在原来的基础上把所有蔬菜卖出去的日子都往前提前,也肯定是可以的——反正我们还是卖了这么多,而且也不会变质。

那么我们就只需要拿一个堆把所有还有库存的蔬菜存起来,每次取出收益最大的把它卡点卖出去。

可能出现日子被占用的情况,我们就往前推时间,总之要尽可能靠后卖,这个可以用并查集很快维护。

参考代码:

#include <cstdio>
#include <queue>
using namespace std;

template < class T > void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

typedef long long LL;
const int _ = 1e6 + 5;

int n, m, k, a[_], s[_], c[_], x[_];
priority_queue < pair < int, int > > Q;
int fa[_], cap[_], cnt; LL ans[_];

int Find(int x) { return fa[x] == x ? x : fa[x] = Find(fa[x]); }

int main() {
#ifndef ONLINE_JUDGE
    freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
#endif
    read(n), read(m), read(k);
    for (int i = 1; i <= n; ++i)
        read(a[i]), read(s[i]), read(c[i]), read(x[i]), Q.push(make_pair(a[i] + s[i], i));
    for (int i = 1; i < _; ++i) fa[i] = i, cap[i] = m;
    while (!Q.empty()) {
        int p = Q.top().second, y = Q.top().first, day; Q.pop();
        if (!x[p]) day = Find(_ - 1); else day = Find(min(_ - 1, (c[p] - 1) / x[p] + 1));
        if (!day) continue ;
        if (!--cap[day]) fa[day] = Find(day - 1);
        if (--c[p]) Q.push(make_pair(a[p], p));
        ++cnt, ans[cnt] = ans[cnt - 1] + y;
    }
    for (int o, i = 1; i <= k; ++i)
        read(o), printf("%lld\n", ans[min(cnt, m * o)]);
    return 0;
}

推荐阅读