洛谷 P3619 魔法 题解

原题链接

Solution

这是一道贪心题,可用邻项交换法解决。我们称某个任务排序为 \(P\)\(P\) 能被完成当且仅当存在能被完成的任务排序。我们需要做的是找到一个可能的 \(P\),通过检查 \(P\) 来判断 \(n\) 个任务能否被完成。接下来通过三步分析引出 \(P\) 的构造。

step 1

如果存在能被完成的任务排列,那么其中必有一个排列,它的前半部分的任务均 \(b_i \ge 0\),后半部分的任务均 \(b_i < 0\)

任取一个能被完成的任务排列,取出前半部分 \(b_i<0\) 的任务 \(X\) 和后半部分 \(b_i\ge0\) 的任务 \(Y\),交换两者位置,新的任务排列也可以被完成。持续执行该操作,最终即可得到如上排列。

设前半部分 \(b_i \ge 0\) 的任务排列为 \(pos\),后半部分 \(b_i < 0\) 的任务排列为 \(neg\)

step 2

\(pos\) 能被完成的充分必要条件是 \(pos\)\(t_i\) 非降序排序后的任务排列能被完成。

充分性显然,下说明必要性。考虑一个能被完成的 \(pos\) 排列,取其中两个相邻任务 \(X_i =\{t_i, b_i\}\)\(X_{i+1}=\{t_{i+1},b_{i+1}\}\)\(t_i > t_{i+1}\),设将要做 \(X_i\) 时的时间为 \(T\),有 \(T > t_i\)\(T+b_i>t_{i+1}\),交换 \(X_i\)\(X_{i+1}\) 的位置,由于 \(T>t_{i+1}\)\(T+b_{i+1}>t_i\) 成立,新的任务排列可以被完成。持续执行交换操作,最终可得到按 \(t_i\) 非降序排列的 \(pos\)

step 3

\(neg\) 能被完成的充分必要条件是 \(neg\)\(t_i+b_i\) 非升序排序后的任务排列能被完成。

充分性显然,下说明必要性。考虑一个能被完成的 \(neg\) 排列,取其中两个相邻任务 \(Y_i =\{t_i, b_i\}\)\(Y_{i+1}=\{t_{i+1},b_{i+1}\}\)\(t_i+b_i < t_{i+1}+b_{i+1}\),设将要做 \(Y_i\) 时的时间为 \(T\),有 \(T > t_i\)\(T+b_i>t_{i+1}\),交换 \(Y_i\)\(Y_{i+1}\) 的位置,由于 \(T>t_{i+1}\)\(T+b_{i+1}>t_i\) 成立(结合上述不等式可推),新的任务排列可以被完成。持续执行交换操作,最终可得到按 \(t_i\) 非升序排列的 \(neg\)

结论

通过上述分析,我们成功构造出了一个合法的 \(P\),它的前半部分任务均 \(b_i \ge 0\),其内部任务按 \(t_i\) 非降序排序;它的后半部分任务均 \(b_i<0\),其内部任务按 \(t_i+b_i\) 非升序排序。

细节

  • \(b_i\) 可正可负,在 \(T\) 递减环节中 \(b_i\) 是个负数,不要当成绝对值处理。
  • 题目要求 \(T\) 在任何时刻均为正数,故最后要检查 \(T\) 是否为正。

牢记

  • 多测换行
  • long long

实现

#include <bits/stdc++.h>
using namespace std;
typedef long long i64;
const int MAXN = 1e5 + 10;

struct Pb {
    i64 t, b;
    friend bool operator<(Pb x, Pb y) {
        return x.t < y.t;
    }
} pos[MAXN];
struct Nb {
    i64 t, b;
    friend bool operator<(Nb x, Nb y) {
        return x.t + x.b > y.t + y.b;
    }
} neg[MAXN];

bool solve() {
    i64 T, t, b;
    int n, cntp = 0, cntn = 0;
    scanf("%d%lld", &n, &T);
    if (T <= 0) return false;
    for (int i = 1; i <= n; ++i) {
        scanf("%lld%lld", &t, &b);
        if (b < 0)
            neg[++cntn] = { t, b };
        else
            pos[++cntp] = { t, b };
    }
    sort(pos + 1, pos + cntp + 1);
    for (int i = 1; i <= cntp; ++i) {
        if (pos[i].t >= T)
            return false;
        T += pos[i].b;
    }
    sort(neg + 1, neg + cntn + 1);
    for (int i = 1; i <= cntn; ++i) {
        if (neg[i].t >= T)
            return false;
        T += neg[i].b;
    }
    return T > 0; // 最后的检查
}

int main() {
    int z;
    scanf("%d", &z);
    while (z--)
        printf("%s\n", solve() ? "+1s" : "-1s");
    return 0;
}
posted @ 2026-04-02 21:42  SHUddol  阅读(6)  评论(0)    收藏  举报