题意:
有一颗n个节点的树,每个节点有一个权值k[i],一开始树上节点全是白色的,你可以选择一个白色的节点进行染色,使该节点到根节点路径上的点距离该节点小于k[i]的节点染成黑色。求使该树所有节点变黑的最少操作次数。
思路:
树形结构+贪心
由于染色的节点在你选择的节点和根之间,所以叶子节点你一定会选择,然后从叶子节点到根的回溯上看,你可以在叶子节点可以染色的范围和不能染色到的第一个节点(考虑同叶子节点)中选择一个可以向根染色更近的一个节点进行贪心,记录你的操作次数。
对于只能在白色节点染色的问题,你可以想象对节点染色顺序使从根节点到页节点进行的,所以你无需考虑。
代码:
#include <bits/stdc++.h> typedef long long ll; using namespace std; const ll inf=1000000007; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int ans=0, k[100005]; vector<int> g[100005]; struct w { int fa, ma; }w, w2; struct w dfs(int v) { if(g[v].size()==0) //对叶子节点进行操作。 { ans++; w.ma=0; w.fa=k[v]-1; return w; } struct w pa; //pa.fa表示染色范围还可以向根前进几个节点,pa.ma表示贪心的结果(既再操作一次可以向根前进节点数目的最大值)。 pa.fa=0; pa.ma=0; for(int i=0;i<g[v].size();i++) { w2=dfs(g[v][i]); pa.fa=max(w2.fa,pa.fa); pa.ma=max(w2.ma-1,pa.ma); } pa.ma=max(pa.ma,k[v]); if(pa.fa==0) //染色范围无法再向根前进,说明要再操作一次。 { ans++; pa.fa=pa.ma-1; pa.ma=0; return pa; } pa.fa--; return pa; } int main() { int n; scanf("%d",&n); for(int i=2;i<=n;i++) { int x; scanf("%d",&x); g[x].push_back(i); } for(int i=1;i<=n;i++) { scanf("%d",&k[i]); } dfs(1); cout << ans << endl; return 0; }