CF500F New Year Shopping

题面

题解

给个不要脑子的做法。

因为这道题是物品会存在一段时间,对每个时间点求出 \(0/1\) 背包的值。然后 \(n\) 和背包大小都是 \(4000\) 级别,于是考虑线段树分治。

也就是将所有物品丢到线段树上去,dfs 一遍整棵树,将覆盖当前区间的所有物品全部加到 \(0/1\) 背包里面去,这样就可以在任意时间点得到 \(0/1\) 背包的 dp 数组,回溯的时候撤销当前操作即可。

时间复杂度 \(\mathcal O(n^2 \log p)\)

代码

#include <cstdio>
#include <algorithm>
#include <vector>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if (ch == '-') w = -1, ch = getchar();
	while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int N(4010), M(20010);
struct node { int l, r, c, h; }; std::vector<node> A;
int n, P, Q, Rmax, f[N], stk[20][N], top, ans[M]; std::vector<std::pair<int, int> > Qry[M];
void Insert(int c, int h) { for (int i = 4000; i >= c; i--) f[i] = std::max(f[i], f[i - c] + h); }
void Push() { for (int i = 1; i <= 4000; i++) stk[top + 1][i] = f[i]; ++top; }
void Undo() { --top; for (int i = 1; i <= 4000; i++) f[i] = stk[top][i]; }

void Solve(const std::vector<node> &V, int l = 1, int r = Rmax)
{
	std::vector<node> L, R; int mid = (l + r) >> 1;
	for (auto i : V)
		if (i.l <= l && r <= i.r) Insert(i.c, i.h);
		else
		{
			if (i.l <= mid) L.push_back(i);
			if (mid < i.r) R.push_back(i);
		}
	Push();
	if (l == r) for (auto i : Qry[l]) ans[i.second] = f[i.first];
	else Solve(L, l, mid), Solve(R, mid + 1, r);
	Undo();
}

int main()
{
	n = read(), P = read();
	for (int i = 1, c, h, t; i <= n; i++)
	{
		c = read(), h = read(), Rmax = std::max(Rmax, P - 1 + (t = read()));
		A.push_back((node) {t, t + P - 1, c, h});
	}
	Q = read();
	for (int i = 1, a, b; i <= Q; i++)
		a = read(), b = read(), Qry[a].emplace_back(b, i);
	Solve(A);
	for (int i = 1; i <= Q; i++) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2020-04-23 20:31  xgzc  阅读(263)  评论(0编辑  收藏  举报