【BZOJ4946】【UOJ318】【NOI2017】蔬菜

【题目链接】

【思路要点】

  • 考虑只有一组询问。
  • 我们可以计算出某种蔬菜最后变质的时间\(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;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80538703