--- 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)