牛客-黑白树(树形dp)

题目链接

题面:
在这里插入图片描述
题解:

因为是向上染色,所以叶子节点肯定要选。接下来就是两种情况:
1.节点 i 能不能被子树中的点染色,那么这个点肯定会被选。
2.节点 i 能被子树中的某个点 j 染色,那么就出现了一个问题,i 这个点要不要选,因为我们可以先染 i 点再染 j 点,怎么去处理呢?
我们可以将儿子节点附在父亲节点上,然后去更新父亲节点的最远距离:k[fa]=max(k[fa],k[son]-1)。 这样做可以将那些还没用到的点的剩余价值放在祖先节点,如果那些选择的点无法染到节点 i ,那么此时就可以用那些没被选的点去染。那么怎么去看被选的点能否染到点 i 呢?可以设dp[i] 表示 i 的子树中选的点能延伸到的最远距离。那么就是dp[u]=max(dp[u],dp[v]-1)。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=1e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
struct node
{
    
    
    int to;
    int next;   
}e[MAXN<<1];
int head[MAXN],k[MAXN];
int cnt=0;
int fa[MAXN];
int ans=0;
int dp[MAXN];
void add(int u,int v)
{
    
    
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u,int f)
{
    
    
    for(int i=head[u];i!=-1;i=e[i].next)
    {
    
    
        int v=e[i].to;
        if(v==f) continue;
        dfs(v,u);
        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()
{
    
    
    int n;
    memset(head,-1,sizeof head);
    cin>>n;
    fa[1]=0;
    for(int i=2;i<=n;i++)
    {
    
    
        int x;
        cin>>x;
        add(x,i);
        add(i,x);
        fa[i]=x;
    }
    for(int i=1;i<=n;i++)
    {
    
    
        cin>>k[i];
    }
    dfs(1,0);
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/weixin_45755679/article/details/113747997