[HEOI2015] 兔子与樱花
Description
很久很久之前,森林里住著一群兔子。有一天,兔子們突然決定要去看櫻花。兔子們所在森林里的櫻花樹很特殊。櫻花樹由 \(n\) 個樹枝分叉點組成,編號從 \(0\) 到 \(n-1\),這 \(n\) 個分叉點由 \(n-1\) 個樹枝連接,我們可以把它看成一個有根樹結構,其中 \(0\) 號節點是根節點。這個樹的每個節點上都會有一些櫻花,其中第 \(i\) 個節點有 \(c_i\) 朵櫻花。櫻花樹的每一個節點都有最大的載重 \(m\),對于每一個節點 \(i\),它的兒子節點的個數和 \(i\) 節點上櫻花個數之和不能超過 \(m\),即 \(son(i) + c_i \leq m\),其中 \(son(i)\) 表示 \(i\) 的兒子的個數,如果 \(i\) 為葉子節點,則 \(son(i) = 0\)
現在兔子們覺得櫻花樹上節點太多,希望去掉一些節點。當一個節點被去掉之后,這個節點上的櫻花和它的兒子節點都被連到刪掉節點的父節點上。如果父節點也被刪除,那么就會繼續向上連接,直到第一個沒有被刪除的節點為止。
現在兔子們希望計算在不違背最大載重的情況下,最多能刪除多少節點。
注意根節點不能被刪除,被刪除的節點不被計入載重。
Input
第一行輸入兩個正整數,\(n\) 和 \(m\) 分別表示節點個數和最大載重
第二行 \(n\) 個整數 \(c_i\) ,表示第 \(i\) 個節點上的櫻花個數
接下來 \(n\) 行,每行第一個數 \(k_i\) 表示這個節點的兒子個數,接下來 \(k_i\)個整數表示這個節點兒子的編號
Output
一行一個整數,表示最多能刪除多少節點。
Hint
對于 \(30\%\) 的數據,\(1\leq n\leq 5000, 1\leq m\leq 100, 0\leq c_i\leq 100\)
對于 \(70\%\) 的數據,\(1\leq n\leq 200000, 1\leq m\leq 2000, 0\leq c_i\leq 1000\)
對于 \(100\%\) 的數據,\(1\leq n\leq 2000000, 1\leq m\leq 100000, 0\leq c_i\leq 1000\)
數據保證初始時,每個節點櫻花數與兒子節點個數之和大于 \(0\) 且不超過 \(m\)
Solution
做法:自底向上,貪心的優先刪除每個點的兒子中代價最小的一個。
貪心:以 \(sons[i]+c[i]\) 為 \(i\) 點的代價,每個點我們選取代價最小的刪除,結果一定不會變差。
證明:對于 \(i\) 點和 \(i\) 的兩個兒子 \(j,p\),假設 \(c[j]+sons[j]<c[p]+sons[p]\),由決策包容性,選 \(j\) 優先刪除一定比選 \(p\) 優先刪除更優。
自底向上:從根節點 \(dfs\) ,從葉子結點向上回溯。路上如果遇到能刪除的點就刪,不必考慮其祖先。
證明:設點 \(i\) 的兒子是 \(j\) ,\(j\) 的兄弟是 \(p\) ,\(j\) 還有一個兒子是 \(q\)。
\(dfs\) 的過程中,如果在回溯到 \(j\) 的時候發現可以刪除 \(q\),那么就刪除 \(q\),并更新 \(j\) 本身的代價,這樣可能會導致無法再回溯到 \(i\) 點的時候刪除 \(p\)。
粗略想一下這不是有后效性嘛,但是因為貪心刪了兒子而導致這個點不能再刪,那么我們只會損失一個點,就是該點,而刪除兒子至少會刪除一個,所以不會虧。。
綜上,自底向上的刪除無后效性,滿足貪心性質。
Code
#include<map> #include<cstdio> #include<cctype> #include<algorithm> #define N 2000005int n,m; int ans; int c[N]; int sons[N],cnt; int tot[N],l[N],r[N];inline char nc(){static const int BS=1<<22;static unsigned char buf[BS],*st,*ed;if(st==ed) ed=buf+fread(st=buf,1,BS,stdin);return st==ed?EOF:*st++; } //#define nc getchar inline int getint(){char ch;int res=0;while(!isdigit(ch=nc()));while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch^48);ch=nc();}return res; }bool cmp(int a,int b){return sons[a]+c[a]<sons[b]+c[b]; }void dfs(int now){if(!sons[now]) return;for(int i=l[now];i<=r[now];i++)dfs(tot[i]);std::sort(tot+l[now],tot+r[now]+1,cmp);for(int i=l[now];i<=r[now];i++){if(c[tot[i]]+sons[tot[i]]+c[now]+sons[now]-1<=m){ans++;c[now]+=c[tot[i]];sons[now]+=sons[tot[i]]-1;}else break;} }signed main(){n=getint(),m=getint();for(int i=1;i<=n;i++)c[i]=getint();for(int i=1;i<=n;i++){sons[i]=getint();l[i]=cnt+1;r[i]=cnt+sons[i];for(int j=1;j<=sons[i];j++){int a=getint()+1;tot[++cnt]=a;}}dfs(1);printf("%d\n",ans);return 0; }轉載于:https://www.cnblogs.com/YoungNeal/p/9084704.html
總結
以上是生活随笔為你收集整理的[HEOI2015] 兔子与樱花的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: yii2.0验签组件(jwt)
- 下一篇: C++ code:main参数