洛谷 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;
}

浙公网安备 33010602011771号