洛谷 P3780 [SDOI2017] 苹果树 题解
Description
给定一棵
给定
对于所有数据,
Solution
深度的条件可以转化为免费选一条链,得到链每个点上的一个苹果。
考虑枚举这条链,链将苹果分为三类:链上每个点的一个免费苹果,链左边的与链上剩余付费苹果,链右边的付费苹果。第一类可以简单处理出每个点到根路径上的价值和,对于第二三类考虑 DP 后对每条链合并。
设
- 父亲将链及左边转移给儿子:枚举
的每个儿子 ,设 为 从左到右的第 个儿子。先将 的值拷贝给 ,然后加入 的苹果,对 跑多重背包(可以不选)。由于要减去一个免费的,所以只能加入 个苹果。转移后递归 的子树。 - 儿子将子树转移给父亲:沿用上文的
。递归 的子树后,更新 。由于 处不一定选了苹果,所以要强制加入一个 苹果来更新: 。
实现的时候不用记第二维。多重背包要用单调队列优化,如果您不会可以先去学习 P1776 宝物筛选 。
同理,设
- 父亲将链右边转移给儿子:枚举
的每个儿子 ,设 为 从右到左的第 个儿子。将 的值拷贝给 ,然后递归 的子树。 - 儿子将子树转移给父亲:沿用
。递归 的子树后,复制一份 ,用 节点上的苹果对 跑多重背包(不能不选),然后来更新 即可: 。
为了方便,实现时
时间复杂度
Code
#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
using namespace std;
namespace Milkcat {
typedef long long LL;
typedef pair<LL, LL> pii;
typedef vector<LL> poly;
const int N = 2e4 + 5;
int n, k, u, v, ans, a[N], val[N], sum[N], siz[N];
vector<int> G[N], f[N], g[N];
void update(vector<int>& f, int c, int v) {
vector<int> g(f.size()); deque<int> q;
for (int i = 0; i <= k; i ++) {
while (!q.empty() && i - q.front() > c) q.pop_front();
while (!q.empty() && f[i] - v * i > f[q.back()] - v * q.back()) q.pop_back();
q.push_back(i), g[i] = f[q.front()] + v * (i - q.front());
}
g.swap(f);
}
void dfs1(int u, int fa) {
update(f[u], a[u] - 1, val[u]);
sum[u] = sum[fa] + val[u], siz[u] = 1;
for (int v : G[u]) {
if (v == fa) continue;
f[v] = f[u], dfs1(v, u), siz[u] += siz[v];
for (int i = 1; i <= k; i ++)
f[u][i] = max(f[u][i], f[v][i - 1] + val[v]);
}
}
void dfs2(int u, int fa) {
reverse(G[u].begin(), G[u].end());
for (int v : G[u]) {
if (v == fa) continue;
g[v] = g[u], dfs2(v, u);
vector<int> tmp(g[v]);
update(tmp, a[v] - 1, val[v]);
for (int i = 1; i <= k; i ++)
g[u][i] = max(g[u][i], tmp[i - 1] + val[v]);
}
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i ++) {
cin >> u >> a[i] >> val[i];
if (u) G[u].pb(i), G[i].pb(u);
}
f[1].resize(k + 2), g[1].resize(k + 2);
dfs1(1, 0), dfs2(1, 0);
for (int i = 1; i <= n; i ++) {
if (siz[i] > 1) continue;
for (int j = 0; j <= k; j ++)
ans = max(ans, f[i][j] + g[i][k - j] + sum[i]);
}
cout << ans << '\n';
return 0;
}
void clear() {
f[1].clear(), g[1].clear(), ans = 0;
for (int i = 1; i <= n; i ++) G[i].clear();
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1; cin >> T;
while (T --) Milkcat::main(), Milkcat::clear();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?