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