题目描述
一棵n个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点i对应一个值k[i]。每个点都有一个颜色,初始的时候所有点都是白色的。
你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点i,i必须是白色的,然后i到根的链上(包括节点i与根)所有与节点i距离小于k[i]的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。
输入描述:
第一行一个整数n (1 ≤ n ≤ 10^5) 接下来n-1行,每行一个整数,依次为2号点到n号点父亲的编号。 最后一行n个整数为k[i] (1 ≤ k[i] ≤ 10^5) 样例解释: 对节点3操作,导致节点2与节点3变黑 对节点4操作,导致节点4变黑 对节点1操作,导致节点1变黑
输出描述:
一个数表示最少操作次数
示例1
输入
4 1 2 1 1 2 2 1
输出
3
题解:
一开始以为是红黑树的姐妹黑白树。。
求出最少的操作,用dfs
我们要不断更新染色的最远距离,还要把子节点的染色范围更新的父亲节点
比如1->2->3-.>4->5->6->7
f[2]=5,f[3]=2
2节点就可以直接染色到6
操作完2之后,如果3就已经被染色了,如果3能染色的范围比fa[ 2 ]-1(因为2已经染色了本身,所以减一)还大,那染色范围可以更远
如果fa[3]<fa[2]-1,就把f3的最远距离更新到fa[2]-1
因为染色都是从下向上的。如果一个节点没办法被它子树的节点染色,那这个节点的父亲节点也没办法将它染色,他只能自己染色了
package com.example;
import java.util.*;
public class Main5{
//声明节点个数为n
static int n;
//声明节点i的父节点为parent[i]
static int[] parent;
//声明节点i的染色距离为k[i]
static int[] k;
public static void main(String[] args){
Scanner in = new Scanner(System.in);
//定义n为输入节点的个数为(n-1)
n = in.nextInt();
//定义父节点数组
parent = new int[n];
//声明颜色数组
isBlack = new boolean[n];
parent[0] = -1;
for(int i =1;i<n;i++){
//其实节点默认为2,此处为2-1。默认节点的父节点in.nextInt()-1
int t = in.nextInt()-1;
//默认节点的父节点存入parent数组当中。
parent[i] = t;
}
k = new int[n];
for(int i = 0;i < n;i++){
//输入节点1到节点n的涂色距离(节点已经默认-1,所以此处为k[0]开始。)
k[i] = in.nextInt();
}
// 第一次遍历,自底向上 dp 出每个子树的可延拓长度
dp1();
// 第二次遍历,赋值颜色,并记录操作次数
System.out.println(dp2());
}
//声明颜色数组
static boolean[] isBlack;
static int dp2(){
int result = 0;
for(int i = n -1;i>=0;i--)
//如果i没有被染色
if(!isBlack[i]){
//节点染色的距离赋值为t
int t = k[i];
//节点赋值为now
int now = i;
//如果染色距离t-1>0 该节点存在父节点
while(t-- > 0 && now != -1){
//染色
isBlack[now] = true;
//当前节点赋值为父节点
now = parent[now];
}
result++;
}
return result;
}
static void dp1(){
//将节点由高向低遍历
for(int i = n-1;i>0;i--){
//节点染色的距离赋值为t
int t = k[i];
//节点的父节点赋值为now
int now = parent[i];
//如果染色距离>0 该节点存在父节点
while(--t > 0 && now != -1){
//父节点的染色距离<该节点的染色距离 while判断条件执行后执行(--t) 此处t=t-1
if(k[now] < t){
//把父节点的染色距离赋值为当前节点的染色距离
k[now] = t;
//把i父节点赋值为父节点的父节点
now = parent[now];
}else break;
}
}
}
}