link。
贪心做法。
本题贪心做法的实质就是用整数尽量多地抵消该整数后面的负数。
如果正着做,没有办法考虑全该数后面的所有负数,所以倒着做。
例如当前遍历到了 \(50\),此时序列如下:
\[\dots,50,-50,-10,-20
\]
易得我们 \(50\) 应该抵消的是 \(-10,-20\),而不是前面的 \(-50\),因为我们要使删除的数越少越好。
但是剩下的 \(50 - 10 - 20 = 20\) 也不能空着,抵消一部分的剩下的 \(-50\) 即可。
最终得到:
\[\dots, -30
\]
发现上述操作可以用堆来维护,具体维护方法见代码。
- 时间复杂度:\(\mathcal O ((n + m) \log _ n)\),瓶颈在于堆和二分。
- 空间复杂度:略。
代码:
// 加强版:n,m <= 1000000 代码
#include <bits/stdc++.h>
#define int long long
#define pii pair<int, int>
using namespace std;const int N = 1e6 + 5;int n, m, a[N], b[N], tot;signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> n >> m;for (int i = 1; i <= n; i++) {cin >> a[i];}priority_queue<int> q;for (int i = n; i >= 1; i--) {if (a[i] < 0) {q.push(a[i]);} else {while (q.size() && a[i] >= 0) {a[i] += q.top();q.pop();}if (a[i] < 0) {q.push(a[i]);}}}while (q.size()) {b[++tot] = -q.top();q.pop();}for (int i = 1; i <= tot; i++) {b[i] += b[i - 1];}for (int i = 1; i <= m; i++) {int x;cin >> x;if (x >= b[tot]) {cout << "0\n";continue;}int t = upper_bound(b + 1, b + tot, x) - b - 1;cout << tot - t << "\n";}return 0;
}