题目:

一棵n个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点i对应一个值k[i]。每个点都有一个颜色,初始的时候所有点都是白色的。
你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点i,i必须是白色的,然后i到根的链上(包括节点i与根)所有与节点i距离小于k[i]的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。


做法:

这题处理起来比较神奇。
结点要么自己染色,要么被子树内的结点染色。所以如果所有都染不到,则必选。
是不是只要有能对染色,就不用考虑了?
不是这样的。按题目描述,不可以先对染色再考虑,因为先对染色,再对染色可能更优!!
那么对于存在可以对染色的情况怎么处理呢?
我们将放到父亲上处理!
具体就是:我们用延申的距离更新延申的距离,
那么在考虑结点的时候就能将选的情况照顾到!!


代码:

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define debug(a) cout << #a ": " << a << endl
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
int n, k[N], fa[N];
vector<int> g[N];
int ans, dp[N];
/*dp[u]表示以u为根的子树染完色后,向上延申的长度*/
void dfs(int u){
    for (int i = 0; i < g[u].size(); ++i){
        int v = g[u][i];
        dfs(v);
        dp[u] = max(dp[u], dp[v]-1);
    }
    if (!dp[u]){
        ans++;
        dp[u] = k[u];
    }else{
        k[fa[u]] = max(k[fa[u]], k[u]-1);
    }
}
int main(void){
    IOS;
    cin >> n;
    for (int i = 2; i <= n; ++i){
        int f; cin >> f;
        g[f].push_back(i);
        fa[i] = f;
    }
    for (int i = 1; i <= n; ++i){
        cin >> k[i];
    }
    dfs(1);
    cout << ans << endl;
    return 0;
}