其实大佬们的题解本菜鸡都看不懂,在这里给出另一种形而上的理解,以及期待能有更加完备的数学解释:为什么负净收益序列中可以单纯对收益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; }
感谢刘晟大佬和青竹大佬对本菜鸡的解惑,没有他们就没有这篇博客。