1 条题解
-
0
模拟赛 T2,但是模拟赛题面是无根树。由于不会这个题爆炸了。
给出一棵节点数为 的有根叶向树,根节点为 。你需要在某些节点放置任意数量的元素,每个元素可以被分配给距离 的节点,且最多分配给 个不同节点。要求每个节点至少被分配到一个元素。最小化放置元素的总数目并输出。
,,1 秒,125 MB。
叶向树上的问题,考虑自底向上贪心。
我们声称,元素应当尽量放置在深度较小的节点上。对于节点 ,若其子树中仍存在距离 的点 未分配,则必须在 处放置元素并分配给 。
可以用调整法说明上述策略的正确性。假设存在最优解,将 替换为在其子树中的点 ,并将 的元素分配给 。记 为两点间距离,则 。
现在将 上的元素移动到 ,我们要证明这仍是一个最优解,即说明被 覆盖的元素可以被 覆盖:
- 对 点:根据定义 ,必要性成立;
- 对 子树外:假定 处元素还覆盖了 子树外的节点 ,则满足 。移动到 有 ,故移动不会影响 外的节点;
- 对 子树内:反证法,若存在 使得其原本能被 覆盖,但不能被 覆盖,则其满足 。根据定义, 且 是子树内最深的未分配节点,而这里 比 深,矛盾。所以不存在这样的 。
说明这一做法的正确性后,考虑设计树形 DP 来执行策略。
记 为在 子树内,距离 为 的可分配数, 为子树内距离为 的需求数目。DP 转移时,讨论在 处新增元素,并且在 处将余量 与需要 进行匹配。
一种错误的策略为:将满足 的所有余量—需求关系在当前 LCA 全部匹配。这种贪心会将极好的余量分配给容易满足的需求,从而导致较难满足的需求只能通过放置元素来解决。
事实上,我们只应该在如下两种情况时进行匹配:
- 在 时将其匹配。因为如果再往祖先移动,就有 ,此时 失去匹配机会;
- 存在 时,再向上走就无法处理这一需求,所以必须在 处新建元素 将其匹配。
正确性容易说明,考虑上述过程相当于贡献延后 DP,且所有余量—需求关系最终都会在根节点正确处理。
自底向上 DFS,对于当前节点 有:
- 首先,将儿子的需求上传到 ,距离增加 ,并在 处新增一个距离为 的需求:
- $f_{i+1,x}=\sum\limits_{v \in \operatorname{son}(u)} f_{i,v}$;
- $g_{i+1,x}=\sum\limits_{v \in \operatorname{son}(u)} g_{i,v}$;
- 。
- 接下来处理第一类匹配。倒序枚举 ,并考虑 。匹配的数目由 决定。
- 在处理 后,进行第二类匹配。计算出放置元素的数目 ,然后清空 ,并将剩余元素加入 。
注意在根节点处,我们需要进行剩余匹配。首先将所有 的 和 匹配后,求出剩余需求数目 ,将 加入答案。
按照上述过程实现,可以做到 。
:::::info[代码]
const int N = 1e5 + 5; int n, s, k; vector<int> e[N]; i64 ans, f[21][N], g[21][N]; void dfs(int u, int fa) { for (int v : e[u]) { if (v == fa) continue; dfs(v, u); for (int i = 0; i < k; i++) f[i + 1][u] += f[i][v], g[i + 1][u] += g[i][v]; } g[0][u] = 1; for (int i = k; i >= 0; i--) { for (int j : {k - i, k - i - 1}) { if (j < 0) continue; i64 a = min(g[i][u], f[j][u]); g[i][u] -= a, f[j][u] -= a; } if (i == k && g[k][u] > 0) { i64 a = (g[k][u] + s - 1) / s; ans += a, f[0][u] += a * s - g[k][u], g[k][u] = 0; } } } void _main() { cin >> n >> s >> k; for (int i = 1, u, v; i < n; i++) { cin >> u >> v; e[u].emplace_back(v), e[v].emplace_back(u); } dfs(1, -1); for (int i = k; i >= 0; i--) { for (int j = k - i; j >= 0; j--) { i64 a = min(g[i][1], f[j][1]); g[i][1] -= a, f[j][1] -= a; } } i64 rem = 0; for (int i = 0; i <= k; i++) rem += g[i][1]; cout << ans + (rem + s - 1) / s; }:::::
- 1
信息
- ID
- 2770
- 时间
- 1000ms
- 内存
- 64MiB
- 难度
- 9
- 标签
- 递交数
- 10
- 已通过
- 6
- 上传者