洛谷 P3523 题解

news/2024/10/6 12:32:37

洛谷 P3523 [POI2011] DYN-Dynamite

分析

二分答案,问题转化为:对于给定的 \(K\),选择尽可能少的节点,使得所有关键节点都被「覆盖」。

对于一个关键节点,「覆盖」的定义为:存在一个被选择的点与这个关键节点的距离不大于 \(K\)

方便起见,我们指定 \(1\) 号节点是这棵树的根节点。

我们使用树形 DP 的思路,自底而上、无后效性地求解。换句话说,显然地,当我们考虑一个节点 \(u\) 时,默认 \(u\) 的子树内已经达到最优。

对于每个节点 \(u\),我们维护两个信息:

  • \(f _ u\)\(u\) 的子树内离 \(u\) 最远的 未被覆盖的关键节点\(u\) 之间的距离。特别地,若 \(u\) 的子树内不存在 未被覆盖的关键节点,则 \(f _ u = -\infty\)

  • \(g _ u\)\(u\) 的子树内离 \(u\) 最近的 被选择的节点\(u\) 之间的距离。特别地,若 \(u\) 的子树内不存在 被选择的节点,则 \(g _ u = \infty\)

初始化 \(f _ u = - \infty, g _ u = \infty\),然后从 \(u\) 的儿子节点转移:

\[f _ u = \max _ {v \in \operatorname{son}(u)} \{ f _ v + 1 \}, \\ g _ u = \max _ {v \in \operatorname{son}(u)} \{ g _ v + 1 \}. \]

现在我们对儿子节点的信息进行汇总:

  • \(f _ u + g _ u \le K\),那么 \(u\) 的子树内所有关键节点都被覆盖了,更新 \(f _ u \gets -\infty\)
  • \(d _ u = 1\)\(u\) 本身是一个关键节点,更新 \(f _ u \gets \max \{f _ u, 0\}\)

接下来决策要不要选择 \(u\),我们有贪心结论:当且仅当 \(f _ u = K\) 时选择 \(u\)

略证:必要性显然;充分及最优性则是因为若 \(f _ u < K\) 我们就可以选择 \(u\) 的祖先,而 \(u\) 的祖先一定不比 \(u\) 劣,因为 \(u\) 的祖先可以覆盖更大的范围。

当然 $u = 1 $ 是个例外,根节点没有祖先,所以若 \(f _ 1 \ne -\infty\) 就一定要选 \(1\) 号节点。

如果选择了 \(u\),那么同样也要更新:

\[f _ u \gets -\infty, \\ g _ u \gets 0. \]

总结一下:在 \([0, n]\) 内二分 \(K\),每次判定时自底而上维护、决策,记录选择了几个点。

复杂度分析

二分复杂度为 \(\operatorname{O}(\log n)\),每次树形 DP 判定复杂度为 \(\operatorname{O}(n)\),总时间复杂度为 \(\operatorname{O}(n \log n)\)

实现

递归常数略大,可能需要卡常。

#include <iostream>
#include <vector>const int N = 300'000, INF = 1e9;int n, m, cnt;
bool d[N + 5];
int f[N + 5], g[N + 5];
std::vector<int> edge[N + 5];void dfs(int u, int fa, int k)
{f[u] = -INF;g[u] = INF;for (auto v : edge[u])if (v != fa){dfs(v, u, k);f[u] = std::max(f[u], f[v] + 1);g[u] = std::min(g[u], g[v] + 1);}if (f[u] + g[u] <= k)f[u] = -INF;if (d[u] && g[u] > k)f[u] = std::max(f[u], 0);if (f[u] == k){f[u] = -INF;g[u] = 0;cnt++;}
}bool check(int k)
{cnt = 0;dfs(1, 0, k);if (f[1] >= 0)cnt++;return cnt <= m;
}int binary_search()
{int l = 0, r = n, res = 0;while (l <= r){int mid = (l + r) / 2;if (check(mid)){r = mid - 1;res = mid;}elsel = mid + 1;}return res;
}int main()
{std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);std::cin >> n >> m;for (int i = 1; i <= n; i++)std::cin >> d[i];for (int i = 1; i < n; i++){int u, v;std::cin >> u >> v;edge[u].push_back(v);edge[v].push_back(u);}std::cout << binary_search() << "\n";return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/68293.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

洪海洋的博客自我介绍

欢迎来到洪海洋的博客 我个人的基本信息 1.你的姓名? 如标题所示,洪海洋。英文名则是OCEAN,“海洋”,这一般也会作为我的网名。 2.为什么起这样的名字? emmm...五行缺水 3.描述一下自己? 多元、社恐、耐心 4.为什么这样描述自己? 对于我来说,多元包含很多个领域,比如我…

树上深度和问题 - 换根DP

问题引出: 给出 \(n\) 个点的树,求出分别以不同的 \(i\) 为根时,所有结点深度的和,根节点的深度为 \(0\)。 首先我们有个自然的暴力思路, 也就是以每个节点为根节点做一遍 \(dfs\) 这样的复杂度是 \(O(n^2)\) 级别的, 所以要进行优化 看下图:我们首先假设每个节点具有点权, …

珂朵莉树(ODT)

前言 主要是一种暴力思想。。。 本文来自 wiki 与洛谷题解的整合。 应用 主要是应付随机数据(区间操作) 实现 有几个核心操作。 set实现方法 定义 struct node {intt l,r;//intt:long longmutable intt v;node(const intt &ll,const intt &rr,const intt &vv) : …

高效开发Maven架构设计图解/掌握项目工程自动化技巧(精通篇一)

Maven是一个项目管理和构建自动化工具,主要服务于基于Java的项目。它使用一个名为POM(Project Object Model)的XML文件来描述项目的构建过程、依赖、插件等信息。肖哥弹架构 跟大家“弹弹” 高并发锁, 关注公号回复 mvcc 获得手写数据库事务代码 欢迎 点赞,关注,评论。…

20222406 2024-2025-1 《网络与系统攻防技术》实验一实验报告

20222406 2024-2025-1 《网络与系统攻防技术》实验一实验报告 1.实验内容 本周深入学习了缓冲区溢出相关内容,收获颇丰。 一、理论知识学习学习了缓冲区溢出的基本知识,包括汇编语言,了解了常见的指令如mov(数据传送)、push(压栈)、pop(出栈)、call(调用函数)等的基…

Markdown格式学习

Markdown格式学习 掌握内容 标题:使用 # 来表示不同级别的标题,如 # 表示一级标题,## 表示二级标题 有序列表:使用数字加点来创建,如 1., 2.。 链接:使用 链接描述。 强调:使用 * 或 _ 来表示斜体(两边各一个),使用两个 ** 或 __ 来表示粗体。 表格:使用 | 和 - 来创…

免费在线音频转字幕网站 All In One

免费在线音频转字幕网站 All In One 利用 AI 将语音转成文本 使用 AI 为视频添加字幕 free online Speech to Text websites免费在线音频转字幕网站 All In One利用 AI 将语音转成文本 / 使用 AI 为视频添加字幕free online Speech to Text websites 每天三次免费https://turbo…

英璞来(imprai)LLMs企业级智能助理:让大语言模型应用触手可及

在这个信息爆炸的时代,人工智能和大数据技术正在改变我们的生活。而随着大语言模型的广泛应用,如何快速、高效地将这些模型集成到各种应用场景中,成为了一个亟待面对的问题。今天,我们要向您介绍一款名为英璞来(imprai)的开箱即用的企业级智能助理平台,它能够让您轻松获…