小红闯地下城
小红闯地下城
题目描述
已知地下城是树形结构,一共有 $n$ 个房间,$n−1$ 条双向通路。入口和出口都在 $1$ 号房间,每个房间有 $1$ 只怪物,小红进入房间后必须先击杀怪物。
每个房间有一个通往其他房间的单向传送阵。小红可以使用最多 $1$ 次传送。
当小红通过道路或者传送阵移动到其他房间时,将消耗 $1$ 点体力。击杀 $1$ 只怪物同样也需要 $1$ 点体力。
小红初始有 $x$ 点体力。她想知道,最多可以击杀多少只怪物?请注意,小红最终必须回到 $1$ 号房间离开地下城。击杀的怪物不能复生。
输入描述:
第一行输入两个正整数 $n,x$,用空格隔开,分别代表房间的数量和小红的初始体力。
第二行输入 $n$ 个正整数 $a_i$,代表每个房间的传送阵通向的房间编号。
接下来的 $n−1$ 行,每行输入两个正整数 $u,v$,代表房间 $u$ 和房间 $v$ 有一条通路。
$1 \leq n \leq 200000$
$1 \leq a_i,u,v \leq n$
$1 \leq x \leq 1000000$
输出描述:
一个整数,代表小红可以击杀的怪物最大数量。
示例1
输入
3 2
2 3 1
1 2
2 3
输出
1
说明
小红只有 $2$ 点体力,她首先击杀 $1$ 号房间的怪物,消耗 $1$ 点体力,然后就什么都做不了了(如果离开 $1$ 号房间就回不来了)。
示例2
输入
3 6
1 3 1
1 2
1 3
输出
3
说明
小红安排体力的流程如下:
击杀 $1$ 号房间怪物,消耗 $1$ 体力。
移动到 $2$ 号房间,消耗 $1$ 体力。
击杀 $2$ 号房间怪物,消耗 $1$ 体力。
传送到 $3$ 号房间,消耗 $1$ 体力。
击杀 $3$ 号房间怪物,消耗 $1$ 体力。
移动到 $1$ 号房间,消耗 $1$ 体力。
解题思路
补了下两个星期前的题,下半个学期的星期五晚上被排了课搞得打不了比赛,也没多少时间补题,惨喵。
可以分成两种情况来考虑,不使用传送门,和使用传送门。
先考虑不使用传送门的情况。对于每个被遍历过的点 $u$(不考虑根节点 $1$),在最优解中连接其父节点 $p$ 的无向边 $(u,p)$ 必然恰好经过 $2$ 次。首先如果有向边 $p \to u$ 经过至少 $2$ 次,意味着从 $p$ 往下至少访问了 $2$ 次子树 $u$,显然我们完全可以只经过 $1$ 次的遍历访问子树 $u$ 中的节点,因此 $p \to u$ 最多会经过 $1$ 次。又因为访问子树 $u$ 后需要从 $u$ 回到根节点,因此 $u \to p$ 至少被经过 $1$ 次。因此无向边 $(u,p)$ 恰好被经过 $2$ 次。
假设一共访问了 $x$ 个节点,意味着会经过 $x-1$ 条边,又因为每个点只击杀一次怪物,每条边恰好经过 $2$ 次,因此消耗的体力就是 $x + 2(x-1)$。可以发现消耗的体力只与访问的节点数量有关,而与访问了哪些节点无关,因此若消耗的体力不超过 $m$ 则最多能访问 $\left\lfloor \frac{m+2}{3} \right\rfloor$ 个节点($3x-2 \leq m \Rightarrow x \leq \left\lfloor \frac{m+2}{3} \right\rfloor$)。
再考虑使用传送门的情况。考虑使用第 $i$ 个传送门的最优解的方案,一定包含从 $1$ 到 $i$ 的路径,加上从 $i$ 到 $a_i$ 的传送,加上从 $a_i$ 到 $1$ 的路径。而其余的部分我们可以看作是挂在 $1 \to i$ 和 $a_i \to 1$ 这两条链上的路径。我们先考虑 $1 \to i \to a_i \to 1$ 消耗的体力。定义 $d_i$ 表示从 $1$ 到 $i$ 这条路径上的节点数量($d_1 = 1$)。首先是 $1 \to i$,容易知道经过了 $d_i$ 个节点和 $d_i - 1$ 条边,因此消耗的体力为 $2d_i - 1$。然后是 $i \to a_i$,使用传送门消耗 $1$ 点体力。最后是 $a_i \to 1$,首先经过了 $d_{a_i} - 1$ 条边,但经过的 $d_{a_i}$ 个点中 $1 \to p$($p=\mathrm{lca}(i, a_i)$)路径上的点会被重复经过,共 $d_{p}$ 个点,因此消耗的体力应该为 $d_{a_i} - 1 + d_{a_i} - d_p$。最后总共消耗的体力就是 $s = (2d_i - 1) + (1) + (d_{a_i} - 1 + d_{a_i} - d_p) = 2d_i + 2d_{a_i} - d_p - 1$,且访问过的节点数量为 $t = d_i + d_{a_i} - d_p$。
将剩余的体力记为 $r = m - s$,考虑体力 $r$ 可以访问多少个挂在 $1 \to i$ 和 $a_i \to 1$ 这两条链上的路径的节点。和上面的分析一样,对于每个被访问的点 $u$,边 $(u,p)$ 会被恰好访问 $2$ 次,因此访问一个点消耗的体力就是 $3$,因此最多能访问的节点数量就是 $\min\left\{n - t, \left\lfloor \frac{r}{3} \right\rfloor \right\}$。所以如果使用第 $i$ 个传送门,最多能访问 $t + \min\left\{n - t, \left\lfloor \frac{r}{3} \right\rfloor \right\}$ 个节点,记为 $f_i$。
因此最后的答案就是 $\max\left\{ \left\lfloor \frac{m+2}{3} \right\rfloor, \; \max\limits_{1 \leq i \leq n}\{f_i\} \right\}$。
AC 代码如下,时间复杂度为 $O(n \log{n})$:
#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