ARC096F Sweet Alchemy 题解
题意
- 有 个物品,买一个第 个物品需要花费 元。
- 除了第一个物品外,第 个物品有一个上级物品 。
- 给定 ,设第 个物品买了 个,要求对于 满足 。
- 一共有 元,问最多能买多少个物品。
- ,。
题解
设 表示 的子树大小, 表示 的子树内权值和,则原问题等价于:
有 个物品和一个体积为 的背包。
第 个物品体积为 价值为 个数为 。
求背包能装下的最大价值和。
,。
这是一个经典的多重背包问题,但这里的背包体积和总价值都非常大,因此无论是以体积为状态还是以价值为状态的多重背包都行不通。
注意到物品个数和价值都很小,考虑从这个角度入手。
考虑 满足 :
- 若 拿了 个,体积为 ,价值为 。
- 若 拿了 个,体积为 ,价值为 。
由于价值一样,但前者的体积更小,所以前者一定不劣。
因此按照 从小到大排序后,我们可以每个物品拿 个出来,剩下的一定是从前往后贪心的拿。
此时总价值不超过 ,于是再考虑以总价值为状态的多重背包,即设 为考虑到第 个物品,总价值为 的最小体积。
单调队列优化是 的,二进制拆分则是 的,都可以接受,但后者更好写。
代码
const int N = 57;
int n, p[N], t;
vi e[N];
ll x, d, w[N], v[N], c[N];
void dfs(int x) {
v[x] = 1;
for (int y : e[x])
dfs(y), v[x] += v[y], w[x] += w[y];
}
const ll inf = 1e18;
ll W[N<<3|1], V[N<<3|1], f[N*N*N], s, ans;
int main() {
rd(n), rd(x, d, w[1]), p[1] = 1;
for (int i = 2, f; i <= n; i++)
rd(w[i]), rd(f), e[f].pb(i), p[i] = i, c[i] = d;
dfs(1), c[1] = x / w[1] * v[1];
sort(p + 1, p + n + 1, [&](int i, int j) {
return w[i] * v[j] < w[j] * v[i];
});
for (int i = 1; i <= n; i++) {
int p = ::p[i];
ll c = min(::c[p], (ll)n);
s += c * v[p];
ll k = 0, x = 0;
while (x + (1 << k) <= c)
x += 1 << k, V[++t] = (1 << k) * v[p], W[t] = (1 << k) * w[p], ++k;
if (c - x) V[++t] = (c - x) * v[p], W[t] = (c - x) * w[p];
}
for (int i = 1; i <= s; i++) f[i] = inf;
for (int i = 1, k = 0; i <= t; k += V[i++])
for (int j = k; ~j; j--)
f[j+V[i]] = min(f[j+V[i]], f[j] + W[i]);
for (int i = 0; i <= s; i++)
if (f[i] <= x) {
ll now = i, sum = f[i];
for (int j = 1; j <= n; j++) {
int p = ::p[j];
ll c = min(::c[p] - min(::c[p], (ll)n), (x - sum) / w[p]);
now += c * v[p], sum += c * w[p];
}
ans = max(ans, now);
}
print(ans);
return 0;
}
评论
暂无已公开评论。