嘘~ 正在从服务器偷取页面 . . .

CF436E Cardboard Box 题解


CF436E Cardboard Box 题解

题目链接:Cardboard Box

题意

每个玩过《Cut the Rope》的人都很清楚游戏的玩法。游戏中的所有关卡都分为多个盒子。最初只有一个包含一些关卡的盒子是可用的。玩家需要通过完成关卡来获得星星,收集星星可以解锁包含新关卡的盒子。

假设你是第一次玩《Cut the Rope》。目前你只有第一个盒子的关卡(顺便说一句,它被称为“纸箱盒”)。每个关卡有两个整数特征:\(a_{i}\) — 完成该关卡以获得一颗星所需的时间,\(b_{i}\) — 完成该关卡以获得两颗星所需的时间(\(a_{i} < b_{i}\))。

你想尽快打开下一个盒子。因此,你需要至少获得 \(w\) 颗星。如何实现这一目标?注意,每个关卡只能通过一次:要么获得一颗星,要么获得两颗星。不一定需要通过所有关卡。

省流

\(n\) 个关卡,对每个关卡,你可以花 \(a_i\) 代价得到一颗星,也可以花 \(b_i\) 代价得到两颗星也可以不玩。

问获得 \(w\) 颗星最少需要多少时间。保证 \(a_i < b_i\)

输入格式

第一行包含两个整数 \(n\)\(w\) \((1 \leq n \leq 3 \cdot 10^{5},~1 \leq w \leq 2n)\)

表示第一个盒子中的关卡数量和打开另一个盒子所需的星星数量。

接下来的 \(n\) 行每行包含两个整数 \(a_{i}\)\(b_{i}\) \((1 \leq a_{i} < b_{i} \leq 10^{9})\) 表示第 \(i\) 个关卡的属性。

输出格式

第一行输出一个整数 \(t\) — 打开下一个盒子所需的最短时间。

第二行输出 \(n\) 个数字,不带空格 — 最优方案的描述:

  • 如果你需要通过第 \(i\) 个关卡以获得一颗星,第 \(i\) 个数字应为 1;
  • 如果你需要通过第 \(i\) 个关卡以获得两颗星,第 \(i\) 个数字应为 2;
  • 如果你不需要通过第 \(i\) 个关卡,第 \(i\) 个数字应为 0。

考虑如何从选了 \(i\) 颗星的最优方案拓展到选了 \(i+1\) 颗星的最优方案:

  1. 选择一个没有选的位置 \(i\) ,花 \(a_i\) 选一颗星。
  2. 选一个选了一颗星的位置 \(i\) ,花 \(b_i-a_i\) 选两颗星。
  3. 选一个选了一颗星的位置 \(i\) 和一个没选的位置 \(j\) ,花 \(b_j-a_i\) 改成 \(i\) 不选, \(j\) 选两颗。
  4. 选一个选了两颗星的位置 \(i\) 和一个没选的位置 \(j\) ,花 \(b_j-(b_i-a_i)\) 改成 \(i\) 不选,\(j\) 选两颗。

考虑用 5 个小根堆维护 \((a_i),\ (b_i-a_i),\ (-a_i),\ (b_i),\ -(b_i-a_i))\)

注意我们每次决策的时候可能会改变一个位置的决策,这个时候堆里的一些元素就无效了

可以在每次拿堆顶元素的时候判断一下它的 Type 和所在位置的 Type 是否一致,不一致就扔掉继续拿。

时间复杂度 \(\mathcal{O}(n\log n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(3e5 + 15))

int a[N], b[N], ans[N];
struct node { int x, i; };
bool operator<(const node &x, const node &y) { return x.x > y.x; }
struct qwq
{
    int op; priority_queue<node> q;
    void up(){ while(!q.empty() && ans[q.top().i] != op) q.pop(); }
    void push(const node &x) { q.push(x); }
    node top() { up(); return q.top(); }
    bool empty() { up(); return q.empty(); }
} q1, q2, q3, q4, q5;
void push1(int x) { ans[x] = 1; q2.push({b[x] - a[x], x}); q3.push({-a[x], x}); }
void push2(int x) { ans[x] = 2; q5.push({-(b[x] - a[x]), x}); }
void push0(int x) { ans[x] = 0; q1.push({a[x], x}); q4.push({b[x], x}); }
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n, m, res = 0; cin >> n >> m; rep(i, 1, n) cin >> a[i] >> b[i];
    q1.op = q4.op = 0; q2.op = q3.op = 1; q5.op = 2;
    rep(i, 1, n) { q1.push({a[i], i}); q4.push({b[i], i}); }
    rep(i, 1, m)
    {
        int x, p, q, mn = INF, op = -1;
        if(!q1.empty() && (x = q1.top().x) < mn) { mn = x; p = q1.top().i; op = 1; }
        if(!q2.empty() && (x = q2.top().x) < mn) { mn = x; p = q2.top().i; op = 2; }
        if(!q3.empty() && !q4.empty() && (x = q3.top().x + q4.top().x) < mn) { mn = x; p = q3.top().i; q = q4.top().i; op = 3; }
        if(!q5.empty() && !q4.empty() && (x = q5.top().x + q4.top().x) < mn) { mn = x; p = q5.top().i; q = q4.top().i; op = 4; }
        res += mn; if(op == -1) exit(1);
        else if(op == 1) push1(p);
        else if(op == 2) push2(p);
        else if(op == 3) { push0(p); push2(q); }
        else if(op == 4) { push1(p); push2(q); }
    }
    cout << res << '\n'; rep(i, 1, n) cout << ans[i];
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/fflz5oan


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录