洛谷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;
}
View Code

 

posted @ 2019-08-26 15:11  wangyuchen  阅读(192)  评论(0编辑  收藏  举报