D. Serval and Rooted Tree (樹狀DP)

Codeforce 1153D Serval and Rooted Tree (樹狀DP)

今天我們來看看CF1153D
題目連結

題目
給一棵數,假設有\(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\)

程式碼:

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

posted @ 2020-08-22 19:41  petjelinux  阅读(70)  评论(0编辑  收藏  举报