[B

B - Zero Tree

CodeForces - 274B

第一件最重要的事情,看清楚题目大意,

我先说说我看错的题意,给你一棵n个节点的树,每个节点都有一个值,你可以进行一种操作:

  • 选择一个包含1的子树(这个子树的概念我也弄错了,我以为这个包含1的子树必须延申到叶子节点)
  • 对这棵子树的每一个节点的值+1,或者-1

问最少的操作使得整棵树的值之和为0。

//////////////////////

完全不是这个题意(((φ(◎ロ◎;)φ)))

正确题意:

给你一棵n个节点的树,每个节点都有一个值,你可以进行一种操作:

  • 选择一个包含1的子树,这个子树就是选择任意几个节点相互连接。
  • 对这棵子树的每一个节点+1,或者-1

问最少的操作让这棵树上的每一个节点的值等于0。

题解:

第一种题意,我觉得还是稍微难一点,猜想把所有满足条件的子树的大小求出来,然后背包?

现在来考虑第二种题意,因为必须要节点1,所以把1当作根节点,这样对于叶子节点如果一个叶子节点 (x)(a[x]=y) 那么必须操作 (y) 次,所以把所有叶子节点的操作数求出来向上传递,每一个节点他的操作数如何计算呢?

我们定义 (dp1[u]) 表示这个 (u) 节点进行 (+) 操作的数量

定义 (dp2[u]) 表示这个节点 (u) 进行 (-) 操作的数量

所以 (dp1[u]=max(dp1[v])) (dp2[u]=max(dp2[v]))

最后再加上操作完之后本身要进行操作的数量。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
#define debug(x) printf("debug:%s=%d
",#x,x);
//#define debug(x) cout << #x << ": " << x << endl
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
int head[maxn<<1],nxt[maxn<<1],to[maxn<<1],cnt;
void add(int u,int v){
    ++cnt,to[cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
    ++cnt,to[cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
}
ll a[maxn],dp1[maxn],dp2[maxn];
void dfs(int u,int pre){
    dp1[u]=dp2[u]=0;
    for(int i=head[u];i;i=nxt[i]){
        int v = to[i];
        if(v==pre) continue;
        dfs(v,u);
        dp1[u]=max(dp1[u],dp1[v]);
        dp2[u]=max(dp2[u],dp2[v]);
    }
    a[u]+=dp1[u]-dp2[u];
    if(a[u]>0) dp2[u]+=a[u];
    else dp1[u]+=abs(a[u]);
}

int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    dfs(1,0);
    printf("%lld
",dp1[1]+dp2[1]);
    return 0;
}
原文地址:https://www.cnblogs.com/EchoZQN/p/13334798.html