2017 日照夏令营 day5 t2 tree

题目大意:
给定一棵 个节点的树,树上的每个节点 有一个权值 ,每次操作中你可以选择一个包含1号节点的连通子树,将这个连
通子树上所有节点的权值加上(或减去)一个相同的非负整数,要求将所有节点变为0,且最小化所有加上(或减去)的
非负整数之和

思路:
以1为根,我们应该先把深度大的点变成0,再把深度小的点变成0
换句话来说,我们想让当前点x变为0,应该先把它的儿子都变为0,这是一个递归定义的过程
树形DP
f[i]表示把以i为根的子树都变为0时在i上的最少累加值
g[i]表示把以i为根的子树都变为0的在i上的最少累减值
f[i]=max(f[i],f[v]),g[i]=max(g[i],g[v])
v是i的儿子
设点i的权值为a[i], t=f[i]-g[i]+a[i]
如果t等于0,那么非常好,不用再在i上加减了
如果t大于0,那么我们就要在这个点减去t,即g[i]+=t
如果t小于0 ,那么我们就要在这个点加上t,即f[i]+=t
复杂度O(n)

#include
#include
#include
#define M 300005
using namespace std;
int x,y,n,s;
int a[M],head[M];
long long f[M],g[M];//f[]累加,g[]累减 
struct node{
    int from,to;
}list[M*2];
void add(int x,int y){
    list[++s].from=head[x];
    list[s].to=y;
    head[x]=s;
}
void dfs(int x,int fa){
    for (int i=head[x];i;i=list[i].from){
        int v=list[i].to;
        if (v==fa) continue;
        dfs(v,x);
        g[x]=max(g[x],g[v]);
        f[x]=max(f[x],f[v]);
    }
    long long t=f[x]-g[x]+a[x];
    if (t>0) g[x]+=t;
    else f[x]-=t;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;iscanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    cout<1]+g[1];
    return 0;
}

你可能感兴趣的:(动态规划,树,2017夏令营)