博客园 首页 私信博主 显示目录 隐藏目录 管理

题解 P3545 【[POI2012]HUR-Warehouse Store】

P3545 [POI2012]HUR-Warehouse Store

Describe

一共 \(n\) 天,每天上午会进 \(A_i\) 的物品,中午会有一个客人想要买走 \(B_i\) 的物品,当然你也可以选择不买,问你最后最多可以交易多少次。

数据范围 : \(1 <= n <= 250000, 0 <= A_i, B_i <= 10^9\)

Solution

首先,我们看到题目先会想到 \(01\) 背包。是个正确的作法,但是肯定过不了。

然后,我们考虑到这一定是个贪心/推式子的题目。

但是推式子的题目一般的特性在这里显然不符合(推式子的题目一般不会让你选择)


那接下来考虑怎么去贪心。

第一想法是我们选最小的肯定是最优的,因为就算选了这个最小的而导致其他的不可以选,那也最多只能导致一个不可以选。

因为如果可以导致多个不可以选的话,选了次小的哪一个也会导致更多的不可以选。

所以选择最小的策略是对的,\(sort\) 一般的复杂度我们也可以接受,但是问题来了,我们那些数可以取?

取完当前最小的,那么它前面的一些数说不定不可以再去。

那么处理这些的复杂度就不稳定了,可以构造数据卡掉。


接下来我们想每天上午进的货物会对什么有影响。

这个很显然,它可以对他后面的所有数产生影响,因为到货了就可以卖给别人了。

那是不是可以这么考虑,从后往前去跑,每次去当前可以取的最小的。

因为从后完全就去除了它的后效性,所有正确性也是对的。

但是新增一个数 \(A_i\) 可能很大,一次可以处理多个数。

那你就必须保证后面整个序列是有序的,而且还要维护一个后缀和。

我们考虑维护这个有序数列的复杂度最优,那就是往前加一个数,就把这个数加入到数列里面去。

这样的复杂度最劣是 \(O(n^2)\) 的,还是会炸。


那我们是不是无法从一些顺序上去找到优化的空间。

我们就可以考虑去莽,然后支持撤销,这样也可以保证正确性。

具体的话是这样实现的:

从前往后读入,每次可以取就取,并且把这些取过的记录在单调队列中。

如果当前这个放不下,我们就把他和队列首的元素做比较,不断更新即可。

根据我们的第二个想法,每个前面取过的如果弹出来,因为他的后效性,肯定是可以服务后面的。

那么就结束了。

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 250010;
int n, sum, ans, book[maxn];
priority_queue<pair<int, int>, vector<pair<int, int> > > Q;
struct node {
    int a, b, id;
} e[maxn];
int cmp(node x, node y) {
    return x.b < y.b;
}
signed main() {
    scanf("%lld", &n);
    for(int i = 1; i <= n; ++ i) {
        scanf("%lld", &e[i].a);
        e[i].id = i;
    }
    for(int i = 1; i <= n; ++ i) {
        scanf("%lld", &e[i].b);
    }
    for(int i = 1; i <= n; ++ i) {
        sum += e[i].a;
        if(e[i].b < sum) {
            Q.push(make_pair(e[i].b, e[i].id));
            ++ ans;
            book[i] = 1;
            sum -= e[i].b;
        }
        else if(! Q.empty() && Q.top().first > e[i].b) {
            book[Q.top().second] = 0;
            book[i] = 1;
            sum += e[Q.top().second].b - e[i].b;
            Q.pop(); Q.push(make_pair(e[i].b, e[i].id));
        }
    }
    printf("%lld\n", ans);
    for(int i = 1; i <= n; ++ i) {
        if(book[i]) {
            printf("%d ", i);
        }
    }
    return 0;
}

Ps:本人知道题解前不应该放错误作法,但是觉得这些错误作法也是一些解题的思路,这里用不上,其他地方可能用到上,望批准。

posted @ 2020-10-18 17:32  Flash_plus  阅读(97)  评论(0编辑  收藏  举报