NC13249 黑白树(每日一题 4月8日 树 贪心)

6 篇文章 0 订阅
5 篇文章 0 订阅

NC13249 黑白树

链接:https://ac.nowcoder.com/acm/problem/13249
题解:https://ac.nowcoder.com/discuss/401669

题目描述
一棵 n n n 个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点 i i i 对应一个值 k [ i ] k[i] k[i] 。每个点都有一个颜色,初始的时候所有点都是白色的。
你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点 i i i i i i 必须是白色的,然后 i i i 到根的链上(包括节点 i i i 与根)所有与节点 i i i 距离小于 k [ i ] k[i] k[i] 的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。
输入描述:
第一行一个整数 n ( 1 ≤ n ≤ 1 0 5 ) n (1 ≤ n ≤ 10^5) n(1n105)
接下来 n − 1 n-1 n1 行,每行一个整数,依次为 2 2 2 号点到 n n n 号点父亲的编号。
最后一行 n n n 个整数为 k [ i ] k[i] k[i] ( 1 ≤ k [ i ] ≤ 1 0 5 1 ≤ k[i] ≤ 10^5 1k[i]105)

样例解释:
对节点3操作,导致节点2与节点3变黑
对节点4操作,导致节点4变黑
对节点1操作,导致节点1变黑
输出描述:
一个数表示最少操作次数
输入
4
1
2
1
1 2 2 1
输出
3


思路:
贪心。由题意可知,叶子节点必定要染色。
那么我们贪心的从子节点往上染色,我们看当前节点 x x x 的已染色的子节点有没有能将它覆盖的,如果有那说明这个点已经被染成黑色的了;如果没有那么就要选择当前节点 x x x 进行染色,然后借助这个点更新往上能覆盖的最大范围。
维护当前节点向上能覆盖的最大距离,可以直接维护在数组 k k k 中。
使用 d f s dfs dfs 从子节点向父节点更新,用数组 f f f 维护当前节点 x x x 的已染色的子节点( f i f_i fi大于0)中能向上覆盖的最大距离, f x = m a x ( f i − 1 ) f_x=max(f_i-1) fx=max(fi1) i i i x x x 的所有子节点,不能覆盖 x x x 就对其染色,然后用 k x k_x kx 更新 f x f_x fx
注意理解这里的 k i k_i ki f i f_i fi 的意义。
时间复杂度: O ( n ) O(n) O(n)

Code:

#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 5;
int n, k[N];
vector<int> e[N]; //存图方式,单向的 父亲 -> 孩子
int f[N], ans, x;
void dfs(int u, int fa) {
    for (int i = 0; i < e[u].size(); i++) {
        int v = e[u][i];
        dfs(v, u);
        f[u] = max(f[u], f[v] - 1);
        k[u] = max(k[u], k[v] - 1);
    }
    if (f[u] == 0) {
        f[u] = k[u];
        ans++;
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for (int i = 2; i <= n; i++) {
        cin >> x;
        e[x].push_back(i);
    }
    for (int i = 1; i <= n; i++) cin >> k[i];
    dfs(1, 0);
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值