洛谷同题

其实大佬们的题解本菜鸡都看不懂,在这里给出另一种形而上的理解,以及期待能有更加完备的数学解释:为什么负净收益序列中可以单纯对收益b排序。

只有上面的问题比较复杂。

首先对序列的净收益排序,赚的先打(让资本达到峰值),亏的后打。

然后既然都是赚的,那么就先打简单的,刷小怪,这样随着应对的挑战在上升,资本也在上升,这一步的贪心非常好理解。

那么重头戏来了,对于亏损的怪,迟早也都要打,那么选取什么样的策略呢?

对于负收益序列,能不能采用对c从大到小排列(降序)或者对a降序呢?
毕竟资本在持续减少,先啃硬骨头也就是a大的看起来也有点道理。

想一个简单的的案例:

7 2
5 2
4 3

初始7血,如果先打5 2,那碰到4就碎了,反之则不然。

那么能不能对c降序呢?

7 2
1 0 -1
7 6 -2

所以BOSS情况把对c降序直接卡掉。

所以不能对c降序或者对a降序。

引入一个我创建的概念:判定点

判定点是指:判断当前值是否<=0的时间,即,这种时间点

如下图,最后一个判定点就是减去了的时候

图片说明

考虑最后一个怪的b值(收益),在这个时候,所有的怪都打完毕业了,所以这个b值就是浪费掉的,实际有效的部分是黄色部分,那既然它会浪费掉,那就让它最小

图片说明

图片说明

我们逐级地向上推演,把每个判定点当作最后一个判定点,所以b从后往前看,应该降序:这是让自己在判定点的当前能力值最大的做法。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 7;
const ll mod = 1e9 + 7;
inline ll read() {
    ll s = 0, f = 1;
    char ch;
    do {
        ch = getchar();
        if (ch == '-') f = -1;
    } while (ch < 48 || ch > 57);
    while (ch >= 48 && ch <= 57)
        s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * f;
}
struct p {
    ll a, b, c;
} g[N];
bool cmp(p x, p y) {
    if (x.c >= 0 && y.c < 0) return 1;
    if (x.c < 0 && y.c >= 0) return 0;
    if (x.c >= 0 && y.c >= 0 && x.a != y.a) return x.a < y.a;
    return x.b > y.b;
}
int main() {
    ll T = read();
    while (T--) {
        ll n = read(), m = read();
        for (int i = 0; i < n; ++i)
            g[i].a = read(), g[i].b = read(), g[i].c = g[i].b - g[i].a;
        sort(g, g + n, cmp);
        bool fin = 1;
        for (int i = 0; i < n && fin; ++i) {
            if (m < g[i].a) fin = 0;
            m += g[i].c;
        }
        printf(fin ? "Yes\n" : "No\n");
    }
    return 0;
}

感谢刘晟大佬和青竹大佬对本菜鸡的解惑,没有他们就没有这篇博客。