【题目链接】
【思路要点】
- 考虑只有一组询问。
- 我们可以计算出某种蔬菜最后变质的时间\(f_i\),将这种蔬菜拆分成\(f_i\)或\(f_i+1\)份,前\(f_i-1\)份质量为\(x_i\),价格为\(a_i\),在第1、2……\(f_i-1\)天后就会腐烂;第\(f_i\)份质量为1,价格为\(a_i+s_i\),在第\(f_i\)天后就会腐烂;若还有剩余,剩余的分为一份,价格为\(a_i\),在第\(f_i\)天后会腐烂。
- 显然,这样的转化是等价的。
- 将售卖的过程倒过来,先考虑最后一天的销售方案,然后将该日腐烂的蔬菜重新放入考虑的范围。
- 显然以下贪心是正确的:对于每一天的售卖,选取考虑范围内价格最大的\(M\)个质量的蔬菜进行售卖。
- 用一个优先队列维护考虑的范围,单次询问时间复杂度为\(O(N^2*M*LogN)\),可以得到60分。
- 进一步考虑,我们发现同一种类的蔬菜在当第\(f_i\)份被加入考虑范围后每一天加入考虑范围的量是一定的。
- 我们再用一个优先队列维护每一天蔬菜的固定增量。
- 注意到\(M\)很小,我们每一天只要取出该增量集合中最昂贵的\(M\)个单位的蔬菜加入考虑范围即可。
- 单次询问时间复杂度降至\(O(N*M*LogN)\),可以得到80分。
- 考虑如何从\(P_i=x\)的方案的基础上得到\(P_i=x-1\)的答案,显然我们只要删去答案集合中最小的若干蔬菜,使得答案集合的大小在销售量以内即可。
- 依然用一个优先队列维护答案集合,从最大的询问向前递推即可。
- 时间复杂度\(O(N*M*LogN)\)(\(N\)、\(P_i\)同阶)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct info {int price, mass; }; bool operator < (info a, info b) { return a.price < b.price; } vector <int> a[MAXN]; int n, m, k, p[MAXN], s[MAXN], c[MAXN], r[MAXN], f[MAXN]; int sum[MAXN]; long long ans[MAXN]; void work(int limit) { static priority_queue <info> Heap, Addt, Temp, Ans; for (int i = 1; i <= n; i++) { if (limit + 1 > f[i]) continue; if (r[i] == 0) { Heap.push((info) {p[i] + s[i], 1}); if (c[i] - 1) Heap.push((info) {p[i], c[i] - 1}); } else { int lft = limit * r[i]; Addt.push((info) {p[i], r[i]}); Heap.push((info) {p[i] + s[i], 1}); if (c[i] - lft - 1) Heap.push((info) {p[i], c[i] - lft - 1}); } } for (int j = limit; j >= 1; j--) { for (unsigned i = 0; i < a[j].size(); i++) { int tmp = a[j][i], lft = (j - 1) * r[tmp]; Heap.push((info) {p[tmp] + s[tmp], 1}); if (c[tmp] - lft - 1) Heap.push((info) {p[tmp], c[tmp] - lft - 1}); } int lft = m; while (lft && !Addt.empty()) { info tmp = Addt.top(); Addt.pop(); int used = min(tmp.mass, lft); tmp.mass -= used; lft -= used; Heap.push((info) {tmp.price, used}); Temp.push((info) {tmp.price, used}); if (tmp.mass) Addt.push(tmp); } while (!Temp.empty()) { info tmp = Temp.top(); Temp.pop(); Addt.push(tmp); } int cnt = m; while (cnt && !Heap.empty()) { info tmp = Heap.top(); Heap.pop(); int used = min(tmp.mass, cnt); ans[limit] += 1ll * tmp.price * used; sum[limit] += used; Ans.push((info) {-tmp.price, used}); tmp.mass -= used; cnt -= used; if (tmp.mass) Heap.push(tmp); } for (unsigned i = 0; i < a[j].size(); i++) { int tmp = a[j][i]; Addt.push((info) {p[tmp], r[tmp]}); } } for (int i = limit - 1; i >= 1; i--) { ans[i] = ans[i + 1]; sum[i] = sum[i + 1]; int cnt = max(0, sum[i] - i * m); while (cnt) { info tmp = Ans.top(); Ans.pop(); int used = min(tmp.mass, cnt); ans[i] += 1ll * tmp.price * used; sum[i] -= used; tmp.mass -= used; cnt -= used; if (tmp.mass) Ans.push(tmp); } } } int main() { read(n), read(m), read(k); for (int i = 1; i <= n; i++) { read(p[i]), read(s[i]); read(c[i]), read(r[i]); if (r[i] == 0) f[i] = 1e6; else { f[i] = c[i] / r[i]; if (c[i] % r[i]) f[i]++; if (f[i] < MAXN) a[f[i]].push_back(i); } } work(1e5); for (int i = 1; i <= k; i++) { int x; read(x); printf("%lld\n", ans[x]); } return 0; }