---
# System prepended metadata

title: Codeforce 1153D

---

---
title: Codeforce 1153D
description: "Codeforce 1153D dp tree-dp"
---

<!-- toc -->

# Codeforce 1153D Serval and Rooted Tree (樹狀DP)
今天我們來看看CF1153D
[題目連結](https://codeforces.com/problemset/problem/1153/D)

> **題目**
給一棵數，假設有$k$個葉節點，我們可以給葉節點分配$1$~$k$這些數字，當做這些節點的"值"。
每個非葉節點的點(不妨令為點$u$)的值有可能是所有$u$的子節點的$\min$或$\max$。
令根節點為節點$1$，求根節點可能的最大值。

### 想法
首先有可能想到要做樹狀dp，而每個點的狀態就是:「假設點$u$為根的子樹有$k_u$個葉節點，$dp[u]$為$1$\~$k_u$中的最大可能(如果我們給這$k_u$個葉節點分配$1$\~$k_u$的值)」。
如果點$u$為$\min$，那麼我們只要找出$u$的子節點中$dp$值最小的就好。但是如果點$u$為$\max$，那麼要得到$dp[u]$，我們只需要在『「$dp$值最大的$u$的子節點(令為$v$)」的子樹的葉節點』中分配$k_u$中最大的$k_v$個即可。
但是如果這樣做，我們會需要維護每一個節點的子樹總共有多少個葉節點。因此我們不妨把$dp$陣列儲存的東西改為:$dp[u]$為$k_u-$($1$\~$k_u$中的最大可能)+1，也就是這次是從$k_u$開始往$1$數，看最大值為第幾個數到的。
如此一來，$dp$轉移式即可改為:
$\begin{cases}
dp[u]=\min\{dp[v]\}_{v\in son(u)}\ \text{if u is max}\\
dp[u]=\sum_{v\in son(u)} dp[v]\ \text{if u is min}
\end{cases}$
而最終的答案即為$k-dp[1]+1$

### 程式碼:
```c++
const int _n=3e5+10;
int t,n,s[_n],dpT[_n],k;
VI G[_n];
int dp(int v){
  if(dpT[v])return dpT[v];
  if(SZ(G[v])==0){k++;return dpT[v]=1;}
  if(s[v]==1){dpT[v]=dp(G[v][0]);rep(i,1,SZ(G[v]))dpT[v]=min(dpT[v],dp(G[v][i]));}
  if(s[v]==0)rep(i,0,SZ(G[v]))dpT[v]+=dp(G[v][i]);
  return dpT[v];
}
main(void) {cin.tie(0);ios_base::sync_with_stdio(0);
  cin>>n;rep(i,1,n+1)cin>>s[i];
  rep(i,2,n+1){cin>>t;G[t].pb(i);}
  dp(1);cout<<k-dp(1)+1<<'\n';
  return 0;
}
```
標頭、模板請點Submission看
[Submission](https://codeforces.com/contest/1153/submission/90074890)