小红闯地下城
小红闯地下城
题目描述
已知地下城是树形结构,一共有 个房间, 条双向通路。入口和出口都在 号房间,每个房间有 只怪物,小红进入房间后必须先击杀怪物。
每个房间有一个通往其他房间的单向传送阵。小红可以使用最多 次传送。
当小红通过道路或者传送阵移动到其他房间时,将消耗 点体力。击杀 只怪物同样也需要 点体力。
小红初始有 点体力。她想知道,最多可以击杀多少只怪物?请注意,小红最终必须回到 号房间离开地下城。击杀的怪物不能复生。
输入描述:
第一行输入两个正整数 ,用空格隔开,分别代表房间的数量和小红的初始体力。
第二行输入 个正整数 ,代表每个房间的传送阵通向的房间编号。
接下来的 行,每行输入两个正整数 ,代表房间 和房间 有一条通路。
输出描述:
一个整数,代表小红可以击杀的怪物最大数量。
示例1
输入
3 2
2 3 1
1 2
2 3
输出
1
说明
小红只有 点体力,她首先击杀 号房间的怪物,消耗 点体力,然后就什么都做不了了(如果离开 号房间就回不来了)。
示例2
输入
3 6
1 3 1
1 2
1 3
输出
3
说明
小红安排体力的流程如下:
击杀 号房间怪物,消耗 体力。
移动到 号房间,消耗 体力。
击杀 号房间怪物,消耗 体力。
传送到 号房间,消耗 体力。
击杀 号房间怪物,消耗 体力。
移动到 号房间,消耗 体力。
解题思路
补了下两个星期前的题,下半个学期的星期五晚上被排了课搞得打不了比赛,也没多少时间补题,惨喵。
可以分成两种情况来考虑,不使用传送门,和使用传送门。
先考虑不使用传送门的情况。对于每个被遍历过的点 (不考虑根节点 ),在最优解中连接其父节点 的无向边 必然恰好经过 次。首先如果有向边 经过至少 次,意味着从 往下至少访问了 次子树 ,显然我们完全可以只经过 次的遍历访问子树 中的节点,因此 最多会经过 次。又因为访问子树 后需要从 回到根节点,因此 至少被经过 次。因此无向边 恰好被经过 次。
假设一共访问了 个节点,意味着会经过 条边,又因为每个点只击杀一次怪物,每条边恰好经过 次,因此消耗的体力就是 。可以发现消耗的体力只与访问的节点数量有关,而与访问了哪些节点无关,因此若消耗的体力不超过 则最多能访问 个节点()。
再考虑使用传送门的情况。考虑使用第 个传送门的最优解的方案,一定包含从 到 的路径,加上从 到 的传送,加上从 到 的路径。而其余的部分我们可以看作是挂在 和 这两条链上的路径。我们先考虑 消耗的体力。定义 表示从 到 这条路径上的节点数量()。首先是 ,容易知道经过了 个节点和 条边,因此消耗的体力为 。然后是 ,使用传送门消耗 点体力。最后是 ,首先经过了 条边,但经过的 个点中 ()路径上的点会被重复经过,共 个点,因此消耗的体力应该为 。最后总共消耗的体力就是 ,且访问过的节点数量为 。
将剩余的体力记为 ,考虑体力 可以访问多少个挂在 和 这两条链上的路径的节点。和上面的分析一样,对于每个被访问的点 ,边 会被恰好访问 次,因此访问一个点消耗的体力就是 ,因此最多能访问的节点数量就是 。所以如果使用第 个传送门,最多能访问 个节点,记为 。
因此最后的答案就是 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = N * 2;
int a[N];
int h[N], e[M], ne[M], idx;
int fa[N][18], d[N];
void add(int u, int v) {
e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}
void dfs(int u, int p) {
fa[u][0] = p;
d[u] = d[p] + 1;
for (int i = 1; i <= 17; i++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (v == p) continue;
dfs(v, u);
}
}
int lca(int a, int b) {
if (d[a] < d[b]) swap(a, b);
for (int i = 17; i >= 0; i--) {
if (d[fa[a][i]] >= d[b]) a = fa[a][i];
}
if (a == b) return a;
for (int i = 17; i >= 0; i--) {
if (fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
}
return fa[a][0];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
memset(h, -1, sizeof(h));
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
add(u, v), add(v, u);
}
dfs(1, 0);
int ret = (m + 2) / 3;
for (int i = 1; i <= n; i++) {
int p = lca(i, a[i]);
int s = 2 * d[i] + 2 * d[a[i]] - d[p] - 1;
int t = d[i] + d[a[i]] - d[p];
if (s <= m) ret = max(ret, t + min(n - t, (m - s) / 3));
}
cout << ret;
return 0;
}
参考资料
【题解】牛客小白月赛104文字题解:https://ac.nowcoder.com/discuss/1434170
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18563351
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-11-22 F. A Growing Tree