Codeforces Round #551 (Div. 2) D. Serval and Rooted Tree (树形dp)

时间:2022-05-29 07:04:55

题目:http://codeforces.com/contest/1153/problem/D

题意:给你一棵树,每个节点有一个操作,0代表取子节点中最小的那个值,1代表取子节点中最大的值,叶子节点的话就是自己置一个值,有k个子节点,那么每个子节点的值范围

就是1-k,1-k只能用一次

思路:贪心不好取,我肯定是要排列完才知道当前的值是哪个,但是我可以知道当前节点应该是取子节点中排名第几的那个,从而推出根节点1的排名,然后再输出就行,因为是要从

子节点那里递归上来,所以我们采取树形dp

#include<bits/stdc++.h>
#define mod 1000000007
#define maxn 300005
using namespace std;
vector<int> mp[maxn];
int d[maxn];
int dp[maxn];
int n,z,num;
void dfs(int x){
    int mn=0,mx=-1;
    for(int i=0;i<mp[x].size();i++){
        dfs(mp[x][i]);
        if(mx==-1) mx=dp[mp[x][i]];   //取最大值也就是取排名最小的那一位
        else mx=min(mx,dp[mp[x][i]]);
        mn+=dp[mp[x][i]];  //最小值也就是取最小的那个值,要先计算出包含多少个叶子节点,然后计算
    }
    if(mp[x].size()==0)//如果是叶子节点
    {
      dp[x]=1;//叶子结点排名第一
      num++;
    }
    else{
        if(d[x]==0) dp[x]=mn;   
        else dp[x]=mx;
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>d[i];
    for(int i=2;i<=n;i++){//建图
        cin>>z;
        mp[z].push_back(i);
    }
    dfs(1);
    cout<<num-dp[1]+1;
}