Serval and Rooted Tree(CF1153D)(树形dp)

题意:
题目给出一棵树,以结点 \(1\)为根,树上每个结点都有一个操作码 \(flag\)

  • \(flag = 0\),则这个结点的值为其孩子结点的最小值;
  • \(flag = 1\),则这个结点的值为其孩子结点的最大值;

需要给每个叶子结点分配 \(1\sim k\)的值(每个结点的值不重复)。问根结点的最大值为多少。


想法:
显然,对于直接通过某种方式去分配叶子结点的值是无法做到的。

  • 考虑树形dp的做法。我们可以考虑怎么求出根结点会受到几个叶子结点的影响,然后把最大的几个值分配给这几个结点即可。答案即是 \(k-num+1\)\(k=叶子结点数\) , \(num=影响根结点的叶子数\) )。

  • 设 $dp[i] $表示以结点 \(i\) 为根的子树中,结点 \(i\) 会受到几个叶子结点的影响。那么在转移方程中, 如果\(flag[i]=1\)\(dp[i]=其子树中的dp最小值\);如果 \(flag[i]=0\)\(dp[i]=其子树的dp值之和\)


代码:

int dp[maxn];
vector<int>g[maxn];
int n;
int flag[maxn];

void dfs(int x)
{
    int maxx=0;
    int minx=inf;
    for(int i=0;i<g[x].size();i++){
        int v=g[x][i];
        dfs(v);
        minx=min(minx,dp[v]);
        maxx+=dp[v];
    }
    if(g[x].size()==0){
        dp[x]=1;
    }else{
        if(flag[x]==0){
            dp[x]=maxx;
        }else{
            dp[x]=minx;
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&flag[i]);
    }    
    for(int i=2;i<=n;i++){
        int num;
        scanf("%d",&num);
        g[num].push_back(i);
    }
    dfs(1);
    int sum=0;
    for(int i=1;i<=n;i++){
        if(g[i].size()==0)sum++;
    }
    printf("%d",sum-dp[1]+1);
}
posted @ 2021-01-24 22:08  hachuochuo  阅读(71)  评论(0编辑  收藏  举报