洛谷P5521 梅深不见冬(贪心)
设答案为$f_u$。
对于叶子结点,$f_u=w_u$。
对于非叶子结点,考虑它的所有儿子$v$,设有$m$个,则只要最小化$\max\limits^m_{i=1}((\sum\limits^{i-1}_{j=1}w_{v_j})+f_{v_i})$,$f_u$即为最小化的答案。
那么如何最小化这个东西呢?把所有儿子按$f_v-w_v$降序排序即可。
证明:
考虑两个儿子的情况,我们只需证明按上述方法排序后有$\max(f_1,w_1+f_2)\le \max(f_2,w_2+f_1)$。
若$\max(f_1,w_1+f_2)=f_1,\max(f_2,w_2+f_1)=w_2+f_1$显然成立;
若$\max(f_1,w_1+f_2)=f_1,\max(f_2,w_2+f_1)=f_2$,则$f_2>f_1$,成立;
若$\max(f_1,w_1+f_2)=w_1+f_2,\max(f_2,w_2+f_1)=f_2$,则$f_2<f_1<w_2+f_1$,不可能;
若$\max(f_1,w_1+f_2)=w_1+f_2,\max(f_2,w_2+f_1)=w_2+f_1$,则由$f_1-w_1\ge f_2-w_2$知$w_1+f_2\le w_2+f_1$,成立。
用数学归纳法可知结论对于更多儿子也成立。
#include<cstdio> #include<algorithm> #include<vector> using namespace std; const int N=100050; char rB[1<<21],*rS,*rT,wB[(1<<21)+50]; int wp=-1; inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;} inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;} inline int rd(){ char c=gc(); while(c<48||c>57)c=gc(); int x=c&15; for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15); return x; } short buf[15]; inline void wt(int x){ if(wp>(1<<21))flush(); short l=-1; while(x>9){ buf[++l]=x%10; x/=10; } wB[++wp]=x|48; while(l>=0)wB[++wp]=buf[l--]|48; wB[++wp]=' '; } int a[N],G[N],to[N],nxt[N],f[N]; vector<int> p[N]; inline bool cmp(int x,int y){return f[x]-a[x]>f[y]-a[y];} void dfs(int u){ int i,v,res=0; for(i=G[u];i;i=nxt[i]){ dfs(v=to[i]); p[u].push_back(v); } sort(p[u].begin(),p[u].end(),cmp); for(i=0;i<p[u].size();++i)if(res>=f[p[u][i]])res-=a[p[u][i]]; else{ f[u]+=f[p[u][i]]-res; res=f[p[u][i]]-a[p[u][i]]; } f[u]+=max(0,a[u]-res); } int main(){ int n=rd(),i,u; for(i=1;i<n;++i){ to[i]=i+1;nxt[i]=G[u=rd()];G[u]=i; } for(i=1;i<=n;++i)a[i]=rd(); dfs(1); for(i=1;i<=n;++i)wt(f[i]); flush(); return 0; }