Loading [MathJax]/jax/element/mml/optable/GeneralPunctuation.js

小红闯地下城

小红闯地下城

题目描述

已知地下城是树形结构,一共有 n 个房间,n1 条双向通路。入口和出口都在 1 号房间,每个房间有 1 只怪物,小红进入房间后必须先击杀怪物。

每个房间有一个通往其他房间的单向传送阵。小红可以使用最多 1 次传送。

当小红通过道路或者传送阵移动到其他房间时,将消耗 1 点体力。击杀 1 只怪物同样也需要 1 点体力。

小红初始有 x 点体力。她想知道,最多可以击杀多少只怪物?请注意,小红最终必须回到 1 号房间离开地下城。击杀的怪物不能复生。

输入描述:

第一行输入两个正整数 n,x,用空格隔开,分别代表房间的数量和小红的初始体力。

第二行输入 n 个正整数 ai​,代表每个房间的传送阵通向的房间编号。

接下来的 n1 行,每行输入两个正整数 u,v,代表房间 u 和房间 v 有一条通路。

1n200000

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 个传送门的最优解的方案,一定包含从 1i 的路径,加上从 ia_i 的传送,加上从 a_i1 的路径。而其余的部分我们可以看作是挂在 1 \to ia_i \to 1 这两条链上的路径。我们先考虑 1 \to i \to a_i \to 1 消耗的体力。定义 d_i 表示从 1i 这条路径上的节点数量(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 pp=\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 ia_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

posted @   onlyblues  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2023-11-22 F. A Growing Tree
Web Analytics
点击右上角即可分享
微信分享提示