题目:
一棵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; }